org.apache.activemq.artemis.spi.core.security.scram.StringPrep Maven / Gradle / Ivy
/**
* Copyright 2011 Glenn Maynard
*
* All rights reserved. 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.apache.activemq.artemis.spi.core.security.scram;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* rfc3454 StringPrep, with an implementation of rfc4013 SASLPrep.
*
* StringPrep case folding is unimplemented, as it's not required by SASLPrep.
*/
public class StringPrep {
/**
* A representation of sets of character classes.
*/
protected static class CharClass {
// Each character class is a set of [start,end] tuples; each tuple is represented
// in the mapping as mapping[start] = (end-start+1).
// Invariants:
// - tupleStart is in ascending order.
// - All values in tupleCount are >= 1 (no empty ranges).
// - tupleStart.size() == tupleCount.size().
// - There will be no overlapping ranges.
//
// TreeMap would work well for this, but it was missing basic operations like lowerEntry
// until JDK1.6, which we don't want to depend on.
private final ArrayList tupleStart = new ArrayList<>();
private final ArrayList tupleCount = new ArrayList<>();
static CharClass fromList(int[] charMap) {
SortedMap mapping = new TreeMap<>();
for (int element : charMap)
mapping.put(element, 1);
return new CharClass(mapping);
}
static CharClass fromRanges(int[] charMap) {
// There must be an even number of tuples in RANGES tables.
if ((charMap.length % 2) != 0)
throw new IllegalArgumentException("Invalid character list size");
SortedMap mapping = new TreeMap<>();
for (int i = 0; i < charMap.length; i += 2) {
int start = charMap[i];
int end = charMap[i + 1];
int count = end - start + 1;
mapping.put(start, count);
}
return new CharClass(mapping);
}
static CharClass fromClasses(CharClass... classes) {
SortedMap mapping = new TreeMap<>();
for (CharClass charClass : classes) {
for (int i = 0; i < charClass.tupleStart.size(); ++i) {
int start = charClass.tupleStart.get(i);
int count = charClass.tupleCount.get(i);
mapping.put(start, count);
}
}
return new CharClass(mapping);
}
private CharClass(SortedMap mappings) {
for (Map.Entry pair : mappings.entrySet()) {
int start = pair.getKey();
int count = pair.getValue();
// Coalesce overlapping ranges.
if (tupleStart.size() > 0) {
int prevIndex = tupleStart.size() - 1;
int prevStart = tupleStart.get(prevIndex);
int prevCount = tupleCount.get(prevIndex);
// If the previous tuple is (0,1), and this tuple is (1,5), then
// coalesce into (0,6). If ths previous tuple is (0,3) and this
// tuple is (1,3), coalesce into (0,4).
if (prevStart + prevCount >= start) {
int endPos = start + count;
int newCount = endPos - prevStart;
tupleCount.set(prevIndex, newCount);
continue;
}
}
tupleStart.add(start);
tupleCount.add(count);
}
}
public boolean isCharInClass(int c) {
// Find the first entry in tupleStart which is <= c. Java's binarySearch
// API is a bit braindamaged (it was written only considering search-and-insert),
// so we have to jump some hoops to get this.
int pos = Collections.binarySearch(tupleStart, c);
if (pos >= 0) {
// If pos >= 0, tupleStart[pos] == c. The value is the start of a range,
// so it's included in the class.
return true;
// while(pos > 0 && tupleStart.get(pos-1) == c)
// --pos;
}
// -pos - 1 is the lowest index where tupleStart[pos] > c. If this is the
// first entry, then c is below all entries in the class.
pos = -pos - 1;
if (pos == 0)
return false;
--pos;
// tupleStart[pos] is <= c.
int start = tupleStart.get(pos);
int count = tupleCount.get(pos);
return start <= c && c < start + count;
}
}
/** A.1 Unassigned code points in Unicode 3.2 */
static final CharClass A1 = CharClass.fromRanges(new int[] {0x0221,
0x0221,
0x0234,
0x024F,
0x02AE,
0x02AF,
0x02EF,
0x02FF,
0x0350,
0x035F,
0x0370,
0x0373,
0x0376,
0x0379,
0x037B,
0x037D,
0x037F,
0x0383,
0x038B,
0x038B,
0x038D,
0x038D,
0x03A2,
0x03A2,
0x03CF,
0x03CF,
0x03F7,
0x03FF,
0x0487,
0x0487,
0x04CF,
0x04CF,
0x04F6,
0x04F7,
0x04FA,
0x04FF,
0x0510,
0x0530,
0x0557,
0x0558,
0x0560,
0x0560,
0x0588,
0x0588,
0x058B,
0x0590,
0x05A2,
0x05A2,
0x05BA,
0x05BA,
0x05C5,
0x05CF,
0x05EB,
0x05EF,
0x05F5,
0x060B,
0x060D,
0x061A,
0x061C,
0x061E,
0x0620,
0x0620,
0x063B,
0x063F,
0x0656,
0x065F,
0x06EE,
0x06EF,
0x06FF,
0x06FF,
0x070E,
0x070E,
0x072D,
0x072F,
0x074B,
0x077F,
0x07B2,
0x0900,
0x0904,
0x0904,
0x093A,
0x093B,
0x094E,
0x094F,
0x0955,
0x0957,
0x0971,
0x0980,
0x0984,
0x0984,
0x098D,
0x098E,
0x0991,
0x0992,
0x09A9,
0x09A9,
0x09B1,
0x09B1,
0x09B3,
0x09B5,
0x09BA,
0x09BB,
0x09BD,
0x09BD,
0x09C5,
0x09C6,
0x09C9,
0x09CA,
0x09CE,
0x09D6,
0x09D8,
0x09DB,
0x09DE,
0x09DE,
0x09E4,
0x09E5,
0x09FB,
0x0A01,
0x0A03,
0x0A04,
0x0A0B,
0x0A0E,
0x0A11,
0x0A12,
0x0A29,
0x0A29,
0x0A31,
0x0A31,
0x0A34,
0x0A34,
0x0A37,
0x0A37,
0x0A3A,
0x0A3B,
0x0A3D,
0x0A3D,
0x0A43,
0x0A46,
0x0A49,
0x0A4A,
0x0A4E,
0x0A58,
0x0A5D,
0x0A5D,
0x0A5F,
0x0A65,
0x0A75,
0x0A80,
0x0A84,
0x0A84,
0x0A8C,
0x0A8C,
0x0A8E,
0x0A8E,
0x0A92,
0x0A92,
0x0AA9,
0x0AA9,
0x0AB1,
0x0AB1,
0x0AB4,
0x0AB4,
0x0ABA,
0x0ABB,
0x0AC6,
0x0AC6,
0x0ACA,
0x0ACA,
0x0ACE,
0x0ACF,
0x0AD1,
0x0ADF,
0x0AE1,
0x0AE5,
0x0AF0,
0x0B00,
0x0B04,
0x0B04,
0x0B0D,
0x0B0E,
0x0B11,
0x0B12,
0x0B29,
0x0B29,
0x0B31,
0x0B31,
0x0B34,
0x0B35,
0x0B3A,
0x0B3B,
0x0B44,
0x0B46,
0x0B49,
0x0B4A,
0x0B4E,
0x0B55,
0x0B58,
0x0B5B,
0x0B5E,
0x0B5E,
0x0B62,
0x0B65,
0x0B71,
0x0B81,
0x0B84,
0x0B84,
0x0B8B,
0x0B8D,
0x0B91,
0x0B91,
0x0B96,
0x0B98,
0x0B9B,
0x0B9B,
0x0B9D,
0x0B9D,
0x0BA0,
0x0BA2,
0x0BA5,
0x0BA7,
0x0BAB,
0x0BAD,
0x0BB6,
0x0BB6,
0x0BBA,
0x0BBD,
0x0BC3,
0x0BC5,
0x0BC9,
0x0BC9,
0x0BCE,
0x0BD6,
0x0BD8,
0x0BE6,
0x0BF3,
0x0C00,
0x0C04,
0x0C04,
0x0C0D,
0x0C0D,
0x0C11,
0x0C11,
0x0C29,
0x0C29,
0x0C34,
0x0C34,
0x0C3A,
0x0C3D,
0x0C45,
0x0C45,
0x0C49,
0x0C49,
0x0C4E,
0x0C54,
0x0C57,
0x0C5F,
0x0C62,
0x0C65,
0x0C70,
0x0C81,
0x0C84,
0x0C84,
0x0C8D,
0x0C8D,
0x0C91,
0x0C91,
0x0CA9,
0x0CA9,
0x0CB4,
0x0CB4,
0x0CBA,
0x0CBD,
0x0CC5,
0x0CC5,
0x0CC9,
0x0CC9,
0x0CCE,
0x0CD4,
0x0CD7,
0x0CDD,
0x0CDF,
0x0CDF,
0x0CE2,
0x0CE5,
0x0CF0,
0x0D01,
0x0D04,
0x0D04,
0x0D0D,
0x0D0D,
0x0D11,
0x0D11,
0x0D29,
0x0D29,
0x0D3A,
0x0D3D,
0x0D44,
0x0D45,
0x0D49,
0x0D49,
0x0D4E,
0x0D56,
0x0D58,
0x0D5F,
0x0D62,
0x0D65,
0x0D70,
0x0D81,
0x0D84,
0x0D84,
0x0D97,
0x0D99,
0x0DB2,
0x0DB2,
0x0DBC,
0x0DBC,
0x0DBE,
0x0DBF,
0x0DC7,
0x0DC9,
0x0DCB,
0x0DCE,
0x0DD5,
0x0DD5,
0x0DD7,
0x0DD7,
0x0DE0,
0x0DF1,
0x0DF5,
0x0E00,
0x0E3B,
0x0E3E,
0x0E5C,
0x0E80,
0x0E83,
0x0E83,
0x0E85,
0x0E86,
0x0E89,
0x0E89,
0x0E8B,
0x0E8C,
0x0E8E,
0x0E93,
0x0E98,
0x0E98,
0x0EA0,
0x0EA0,
0x0EA4,
0x0EA4,
0x0EA6,
0x0EA6,
0x0EA8,
0x0EA9,
0x0EAC,
0x0EAC,
0x0EBA,
0x0EBA,
0x0EBE,
0x0EBF,
0x0EC5,
0x0EC5,
0x0EC7,
0x0EC7,
0x0ECE,
0x0ECF,
0x0EDA,
0x0EDB,
0x0EDE,
0x0EFF,
0x0F48,
0x0F48,
0x0F6B,
0x0F70,
0x0F8C,
0x0F8F,
0x0F98,
0x0F98,
0x0FBD,
0x0FBD,
0x0FCD,
0x0FCE,
0x0FD0,
0x0FFF,
0x1022,
0x1022,
0x1028,
0x1028,
0x102B,
0x102B,
0x1033,
0x1035,
0x103A,
0x103F,
0x105A,
0x109F,
0x10C6,
0x10CF,
0x10F9,
0x10FA,
0x10FC,
0x10FF,
0x115A,
0x115E,
0x11A3,
0x11A7,
0x11FA,
0x11FF,
0x1207,
0x1207,
0x1247,
0x1247,
0x1249,
0x1249,
0x124E,
0x124F,
0x1257,
0x1257,
0x1259,
0x1259,
0x125E,
0x125F,
0x1287,
0x1287,
0x1289,
0x1289,
0x128E,
0x128F,
0x12AF,
0x12AF,
0x12B1,
0x12B1,
0x12B6,
0x12B7,
0x12BF,
0x12BF,
0x12C1,
0x12C1,
0x12C6,
0x12C7,
0x12CF,
0x12CF,
0x12D7,
0x12D7,
0x12EF,
0x12EF,
0x130F,
0x130F,
0x1311,
0x1311,
0x1316,
0x1317,
0x131F,
0x131F,
0x1347,
0x1347,
0x135B,
0x1360,
0x137D,
0x139F,
0x13F5,
0x1400,
0x1677,
0x167F,
0x169D,
0x169F,
0x16F1,
0x16FF,
0x170D,
0x170D,
0x1715,
0x171F,
0x1737,
0x173F,
0x1754,
0x175F,
0x176D,
0x176D,
0x1771,
0x1771,
0x1774,
0x177F,
0x17DD,
0x17DF,
0x17EA,
0x17FF,
0x180F,
0x180F,
0x181A,
0x181F,
0x1878,
0x187F,
0x18AA,
0x1DFF,
0x1E9C,
0x1E9F,
0x1EFA,
0x1EFF,
0x1F16,
0x1F17,
0x1F1E,
0x1F1F,
0x1F46,
0x1F47,
0x1F4E,
0x1F4F,
0x1F58,
0x1F58,
0x1F5A,
0x1F5A,
0x1F5C,
0x1F5C,
0x1F5E,
0x1F5E,
0x1F7E,
0x1F7F,
0x1FB5,
0x1FB5,
0x1FC5,
0x1FC5,
0x1FD4,
0x1FD5,
0x1FDC,
0x1FDC,
0x1FF0,
0x1FF1,
0x1FF5,
0x1FF5,
0x1FFF,
0x1FFF,
0x2053,
0x2056,
0x2058,
0x205E,
0x2064,
0x2069,
0x2072,
0x2073,
0x208F,
0x209F,
0x20B2,
0x20CF,
0x20EB,
0x20FF,
0x213B,
0x213C,
0x214C,
0x2152,
0x2184,
0x218F,
0x23CF,
0x23FF,
0x2427,
0x243F,
0x244B,
0x245F,
0x24FF,
0x24FF,
0x2614,
0x2615,
0x2618,
0x2618,
0x267E,
0x267F,
0x268A,
0x2700,
0x2705,
0x2705,
0x270A,
0x270B,
0x2728,
0x2728,
0x274C,
0x274C,
0x274E,
0x274E,
0x2753,
0x2755,
0x2757,
0x2757,
0x275F,
0x2760,
0x2795,
0x2797,
0x27B0,
0x27B0,
0x27BF,
0x27CF,
0x27EC,
0x27EF,
0x2B00,
0x2E7F,
0x2E9A,
0x2E9A,
0x2EF4,
0x2EFF,
0x2FD6,
0x2FEF,
0x2FFC,
0x2FFF,
0x3040,
0x3040,
0x3097,
0x3098,
0x3100,
0x3104,
0x312D,
0x3130,
0x318F,
0x318F,
0x31B8,
0x31EF,
0x321D,
0x321F,
0x3244,
0x3250,
0x327C,
0x327E,
0x32CC,
0x32CF,
0x32FF,
0x32FF,
0x3377,
0x337A,
0x33DE,
0x33DF,
0x33FF,
0x33FF,
0x4DB6,
0x4DFF,
0x9FA6,
0x9FFF,
0xA48D,
0xA48F,
0xA4C7,
0xABFF,
0xD7A4,
0xD7FF,
0xFA2E,
0xFA2F,
0xFA6B,
0xFAFF,
0xFB07,
0xFB12,
0xFB18,
0xFB1C,
0xFB37,
0xFB37,
0xFB3D,
0xFB3D,
0xFB3F,
0xFB3F,
0xFB42,
0xFB42,
0xFB45,
0xFB45,
0xFBB2,
0xFBD2,
0xFD40,
0xFD4F,
0xFD90,
0xFD91,
0xFDC8,
0xFDCF,
0xFDFD,
0xFDFF,
0xFE10,
0xFE1F,
0xFE24,
0xFE2F,
0xFE47,
0xFE48,
0xFE53,
0xFE53,
0xFE67,
0xFE67,
0xFE6C,
0xFE6F,
0xFE75,
0xFE75,
0xFEFD,
0xFEFE,
0xFF00,
0xFF00,
0xFFBF,
0xFFC1,
0xFFC8,
0xFFC9,
0xFFD0,
0xFFD1,
0xFFD8,
0xFFD9,
0xFFDD,
0xFFDF,
0xFFE7,
0xFFE7,
0xFFEF,
0xFFF8,
0x10000,
0x102FF,
0x1031F,
0x1031F,
0x10324,
0x1032F,
0x1034B,
0x103FF,
0x10426,
0x10427,
0x1044E,
0x1CFFF,
0x1D0F6,
0x1D0FF,
0x1D127,
0x1D129,
0x1D1DE,
0x1D3FF,
0x1D455,
0x1D455,
0x1D49D,
0x1D49D,
0x1D4A0,
0x1D4A1,
0x1D4A3,
0x1D4A4,
0x1D4A7,
0x1D4A8,
0x1D4AD,
0x1D4AD,
0x1D4BA,
0x1D4BA,
0x1D4BC,
0x1D4BC,
0x1D4C1,
0x1D4C1,
0x1D4C4,
0x1D4C4,
0x1D506,
0x1D506,
0x1D50B,
0x1D50C,
0x1D515,
0x1D515,
0x1D51D,
0x1D51D,
0x1D53A,
0x1D53A,
0x1D53F,
0x1D53F,
0x1D545,
0x1D545,
0x1D547,
0x1D549,
0x1D551,
0x1D551,
0x1D6A4,
0x1D6A7,
0x1D7CA,
0x1D7CD,
0x1D800,
0x1FFFD,
0x2A6D7,
0x2F7FF,
0x2FA1E,
0x2FFFD,
0x30000,
0x3FFFD,
0x40000,
0x4FFFD,
0x50000,
0x5FFFD,
0x60000,
0x6FFFD,
0x70000,
0x7FFFD,
0x80000,
0x8FFFD,
0x90000,
0x9FFFD,
0xA0000,
0xAFFFD,
0xB0000,
0xBFFFD,
0xC0000,
0xCFFFD,
0xD0000,
0xDFFFD,
0xE0000,
0xE0000,
0xE0002,
0xE001F,
0xE0080,
0xEFFFD,});
/** B.1 Commonly mapped to nothing */
static final CharClass B1 = CharClass.fromList(new int[] {0x00AD,
0x034F,
0x1806,
0x180B,
0x180C,
0x180D,
0x200B,
0x200C,
0x200D,
0x2060,
0xFE00,
0xFE01,
0xFE02,
0xFE03,
0xFE04,
0xFE05,
0xFE06,
0xFE07,
0xFE08,
0xFE09,
0xFE0A,
0xFE0B,
0xFE0C,
0xFE0D,
0xFE0E,
0xFE0F,
0xFEFF,});
/** C.1.1 ASCII space characters */
static final CharClass C11 = CharClass.fromList(new int[] {0x0020});
/** C.1.2 Non-ASCII space characters */
static final CharClass C12 = CharClass.fromList(new int[] {0x00A0,
0x1680,
0x2000,
0x2001,
0x2002,
0x2003,
0x2004,
0x2005,
0x2006,
0x2007,
0x2008,
0x2009,
0x200A,
0x200B,
0x202F,
0x205F,
0x3000,});
/** C.2.1 ASCII control characters */
static final CharClass C21 = CharClass.fromList(new int[] {0x0000,
0x0001,
0x0002,
0x0003,
0x0004,
0x0005,
0x0006,
0x0007,
0x0008,
0x0009,
0x000A,
0x000B,
0x000C,
0x000D,
0x000E,
0x000F,
0x0010,
0x0011,
0x0012,
0x0013,
0x0014,
0x0015,
0x0016,
0x0017,
0x0018,
0x0019,
0x001A,
0x001B,
0x001C,
0x001D,
0x001E,
0x001F,
0x007F});
/** C.2.2 Non-ASCII control characters */
static final CharClass C22 = CharClass.fromList(new int[] {0x0080,
0x0081,
0x0082,
0x0083,
0x0084,
0x0085,
0x0086,
0x0087,
0x0088,
0x0089,
0x008A,
0x008B,
0x008C,
0x008D,
0x008E,
0x008F,
0x0090,
0x0091,
0x0092,
0x0093,
0x0094,
0x0095,
0x0096,
0x0097,
0x0098,
0x0099,
0x009A,
0x009B,
0x009C,
0x009D,
0x009E,
0x009F,
0x06DD,
0x070F,
0x180E,
0x200C,
0x200D,
0x2028,
0x2029,
0x2060,
0x2061,
0x2062,
0x2063,
0x206A,
0x206B,
0x206C,
0x206D,
0x206E,
0x206F,
0xFEFF,
0xFFF9,
0xFFFA,
0xFFFB,
0xFFFC,
0x1D173,
0x1D174,
0x1D175,
0x1D176,
0x1D177,
0x1D178,
0x1D179,
0x1D17A,});
/** C.3 Private use */
static final CharClass C3 = CharClass.fromRanges(new int[] {0xE000, 0xF8FF, 0xF0000, 0xFFFFD, 0x100000, 0x10FFFD,});
/** C.4 Non-character code points */
static final CharClass C4 = CharClass.fromRanges(new int[] {0xFDD0,
0xFDEF,
0xFFFE,
0xFFFF,
0x1FFFE,
0x1FFFF,
0x2FFFE,
0x2FFFF,
0x3FFFE,
0x3FFFF,
0x4FFFE,
0x4FFFF,
0x5FFFE,
0x5FFFF,
0x6FFFE,
0x6FFFF,
0x7FFFE,
0x7FFFF,
0x8FFFE,
0x8FFFF,
0x9FFFE,
0x9FFFF,
0xAFFFE,
0xAFFFF,
0xBFFFE,
0xBFFFF,
0xCFFFE,
0xCFFFF,
0xDFFFE,
0xDFFFF,
0xEFFFE,
0xEFFFF,
0xFFFFE,
0xFFFFF,
0x10FFFE,
0x10FFFF,});
/** C.5 Surrogate codes */
static final CharClass C5 = CharClass.fromRanges(new int[] {0xD800, 0xDFFF,});
/** C.6 Inappropriate for plain text */
static final CharClass C6 = CharClass.fromList(new int[] {0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD,});
/** C.7 Inappropriate for canonical representation */
static final CharClass C7 = CharClass.fromList(new int[] {0x2FF0,
0x2FF1,
0x2FF2,
0x2FF3,
0x2FF4,
0x2FF5,
0x2FF6,
0x2FF7,
0x2FF8,
0x2FF9,
0x2FFA,
0x2FFB,});
/** C.8 Change display properties or are deprecated */
static final CharClass C8 = CharClass.fromList(new int[] {0x0340,
0x0341,
0x200E,
0x200F,
0x202A,
0x202B,
0x202C,
0x202D,
0x202E,
0x206A,
0x206B,
0x206C,
0x206D,
0x206E,
0x206F,});
/** C.9 Tagging characters (tuples) */
static final CharClass C9 = CharClass.fromRanges(new int[] {0xE0001, 0xE0001, 0xE0020, 0xE007F,});
/** D.1 Characters with bidirectional property "R" or "AL" */
static final CharClass D1 = CharClass.fromRanges(new int[] {0x05BE,
0x05BE,
0x05C0,
0x05C0,
0x05C3,
0x05C3,
0x05D0,
0x05EA,
0x05F0,
0x05F4,
0x061B,
0x061B,
0x061F,
0x061F,
0x0621,
0x063A,
0x0640,
0x064A,
0x066D,
0x066F,
0x0671,
0x06D5,
0x06DD,
0x06DD,
0x06E5,
0x06E6,
0x06FA,
0x06FE,
0x0700,
0x070D,
0x0710,
0x0710,
0x0712,
0x072C,
0x0780,
0x07A5,
0x07B1,
0x07B1,
0x200F,
0x200F,
0xFB1D,
0xFB1D,
0xFB1F,
0xFB28,
0xFB2A,
0xFB36,
0xFB38,
0xFB3C,
0xFB3E,
0xFB3E,
0xFB40,
0xFB41,
0xFB43,
0xFB44,
0xFB46,
0xFBB1,
0xFBD3,
0xFD3D,
0xFD50,
0xFD8F,
0xFD92,
0xFDC7,
0xFDF0,
0xFDFC,
0xFE70,
0xFE74,
0xFE76,
0xFEFC,});
/** D.2 Characters with bidirectional property "L" */
static final CharClass D2 = CharClass.fromRanges(new int[] {0x0041,
0x005A,
0x0061,
0x007A,
0x00AA,
0x00AA,
0x00B5,
0x00B5,
0x00BA,
0x00BA,
0x00C0,
0x00D6,
0x00D8,
0x00F6,
0x00F8,
0x0220,
0x0222,
0x0233,
0x0250,
0x02AD,
0x02B0,
0x02B8,
0x02BB,
0x02C1,
0x02D0,
0x02D1,
0x02E0,
0x02E4,
0x02EE,
0x02EE,
0x037A,
0x037A,
0x0386,
0x0386,
0x0388,
0x038A,
0x038C,
0x038C,
0x038E,
0x03A1,
0x03A3,
0x03CE,
0x03D0,
0x03F5,
0x0400,
0x0482,
0x048A,
0x04CE,
0x04D0,
0x04F5,
0x04F8,
0x04F9,
0x0500,
0x050F,
0x0531,
0x0556,
0x0559,
0x055F,
0x0561,
0x0587,
0x0589,
0x0589,
0x0903,
0x0903,
0x0905,
0x0939,
0x093D,
0x0940,
0x0949,
0x094C,
0x0950,
0x0950,
0x0958,
0x0961,
0x0964,
0x0970,
0x0982,
0x0983,
0x0985,
0x098C,
0x098F,
0x0990,
0x0993,
0x09A8,
0x09AA,
0x09B0,
0x09B2,
0x09B2,
0x09B6,
0x09B9,
0x09BE,
0x09C0,
0x09C7,
0x09C8,
0x09CB,
0x09CC,
0x09D7,
0x09D7,
0x09DC,
0x09DD,
0x09DF,
0x09E1,
0x09E6,
0x09F1,
0x09F4,
0x09FA,
0x0A05,
0x0A0A,
0x0A0F,
0x0A10,
0x0A13,
0x0A28,
0x0A2A,
0x0A30,
0x0A32,
0x0A33,
0x0A35,
0x0A36,
0x0A38,
0x0A39,
0x0A3E,
0x0A40,
0x0A59,
0x0A5C,
0x0A5E,
0x0A5E,
0x0A66,
0x0A6F,
0x0A72,
0x0A74,
0x0A83,
0x0A83,
0x0A85,
0x0A8B,
0x0A8D,
0x0A8D,
0x0A8F,
0x0A91,
0x0A93,
0x0AA8,
0x0AAA,
0x0AB0,
0x0AB2,
0x0AB3,
0x0AB5,
0x0AB9,
0x0ABD,
0x0AC0,
0x0AC9,
0x0AC9,
0x0ACB,
0x0ACC,
0x0AD0,
0x0AD0,
0x0AE0,
0x0AE0,
0x0AE6,
0x0AEF,
0x0B02,
0x0B03,
0x0B05,
0x0B0C,
0x0B0F,
0x0B10,
0x0B13,
0x0B28,
0x0B2A,
0x0B30,
0x0B32,
0x0B33,
0x0B36,
0x0B39,
0x0B3D,
0x0B3E,
0x0B40,
0x0B40,
0x0B47,
0x0B48,
0x0B4B,
0x0B4C,
0x0B57,
0x0B57,
0x0B5C,
0x0B5D,
0x0B5F,
0x0B61,
0x0B66,
0x0B70,
0x0B83,
0x0B83,
0x0B85,
0x0B8A,
0x0B8E,
0x0B90,
0x0B92,
0x0B95,
0x0B99,
0x0B9A,
0x0B9C,
0x0B9C,
0x0B9E,
0x0B9F,
0x0BA3,
0x0BA4,
0x0BA8,
0x0BAA,
0x0BAE,
0x0BB5,
0x0BB7,
0x0BB9,
0x0BBE,
0x0BBF,
0x0BC1,
0x0BC2,
0x0BC6,
0x0BC8,
0x0BCA,
0x0BCC,
0x0BD7,
0x0BD7,
0x0BE7,
0x0BF2,
0x0C01,
0x0C03,
0x0C05,
0x0C0C,
0x0C0E,
0x0C10,
0x0C12,
0x0C28,
0x0C2A,
0x0C33,
0x0C35,
0x0C39,
0x0C41,
0x0C44,
0x0C60,
0x0C61,
0x0C66,
0x0C6F,
0x0C82,
0x0C83,
0x0C85,
0x0C8C,
0x0C8E,
0x0C90,
0x0C92,
0x0CA8,
0x0CAA,
0x0CB3,
0x0CB5,
0x0CB9,
0x0CBE,
0x0CBE,
0x0CC0,
0x0CC4,
0x0CC7,
0x0CC8,
0x0CCA,
0x0CCB,
0x0CD5,
0x0CD6,
0x0CDE,
0x0CDE,
0x0CE0,
0x0CE1,
0x0CE6,
0x0CEF,
0x0D02,
0x0D03,
0x0D05,
0x0D0C,
0x0D0E,
0x0D10,
0x0D12,
0x0D28,
0x0D2A,
0x0D39,
0x0D3E,
0x0D40,
0x0D46,
0x0D48,
0x0D4A,
0x0D4C,
0x0D57,
0x0D57,
0x0D60,
0x0D61,
0x0D66,
0x0D6F,
0x0D82,
0x0D83,
0x0D85,
0x0D96,
0x0D9A,
0x0DB1,
0x0DB3,
0x0DBB,
0x0DBD,
0x0DBD,
0x0DC0,
0x0DC6,
0x0DCF,
0x0DD1,
0x0DD8,
0x0DDF,
0x0DF2,
0x0DF4,
0x0E01,
0x0E30,
0x0E32,
0x0E33,
0x0E40,
0x0E46,
0x0E4F,
0x0E5B,
0x0E81,
0x0E82,
0x0E84,
0x0E84,
0x0E87,
0x0E88,
0x0E8A,
0x0E8A,
0x0E8D,
0x0E8D,
0x0E94,
0x0E97,
0x0E99,
0x0E9F,
0x0EA1,
0x0EA3,
0x0EA5,
0x0EA5,
0x0EA7,
0x0EA7,
0x0EAA,
0x0EAB,
0x0EAD,
0x0EB0,
0x0EB2,
0x0EB3,
0x0EBD,
0x0EBD,
0x0EC0,
0x0EC4,
0x0EC6,
0x0EC6,
0x0ED0,
0x0ED9,
0x0EDC,
0x0EDD,
0x0F00,
0x0F17,
0x0F1A,
0x0F34,
0x0F36,
0x0F36,
0x0F38,
0x0F38,
0x0F3E,
0x0F47,
0x0F49,
0x0F6A,
0x0F7F,
0x0F7F,
0x0F85,
0x0F85,
0x0F88,
0x0F8B,
0x0FBE,
0x0FC5,
0x0FC7,
0x0FCC,
0x0FCF,
0x0FCF,
0x1000,
0x1021,
0x1023,
0x1027,
0x1029,
0x102A,
0x102C,
0x102C,
0x1031,
0x1031,
0x1038,
0x1038,
0x1040,
0x1057,
0x10A0,
0x10C5,
0x10D0,
0x10F8,
0x10FB,
0x10FB,
0x1100,
0x1159,
0x115F,
0x11A2,
0x11A8,
0x11F9,
0x1200,
0x1206,
0x1208,
0x1246,
0x1248,
0x1248,
0x124A,
0x124D,
0x1250,
0x1256,
0x1258,
0x1258,
0x125A,
0x125D,
0x1260,
0x1286,
0x1288,
0x1288,
0x128A,
0x128D,
0x1290,
0x12AE,
0x12B0,
0x12B0,
0x12B2,
0x12B5,
0x12B8,
0x12BE,
0x12C0,
0x12C0,
0x12C2,
0x12C5,
0x12C8,
0x12CE,
0x12D0,
0x12D6,
0x12D8,
0x12EE,
0x12F0,
0x130E,
0x1310,
0x1310,
0x1312,
0x1315,
0x1318,
0x131E,
0x1320,
0x1346,
0x1348,
0x135A,
0x1361,
0x137C,
0x13A0,
0x13F4,
0x1401,
0x1676,
0x1681,
0x169A,
0x16A0,
0x16F0,
0x1700,
0x170C,
0x170E,
0x1711,
0x1720,
0x1731,
0x1735,
0x1736,
0x1740,
0x1751,
0x1760,
0x176C,
0x176E,
0x1770,
0x1780,
0x17B6,
0x17BE,
0x17C5,
0x17C7,
0x17C8,
0x17D4,
0x17DA,
0x17DC,
0x17DC,
0x17E0,
0x17E9,
0x1810,
0x1819,
0x1820,
0x1877,
0x1880,
0x18A8,
0x1E00,
0x1E9B,
0x1EA0,
0x1EF9,
0x1F00,
0x1F15,
0x1F18,
0x1F1D,
0x1F20,
0x1F45,
0x1F48,
0x1F4D,
0x1F50,
0x1F57,
0x1F59,
0x1F59,
0x1F5B,
0x1F5B,
0x1F5D,
0x1F5D,
0x1F5F,
0x1F7D,
0x1F80,
0x1FB4,
0x1FB6,
0x1FBC,
0x1FBE,
0x1FBE,
0x1FC2,
0x1FC4,
0x1FC6,
0x1FCC,
0x1FD0,
0x1FD3,
0x1FD6,
0x1FDB,
0x1FE0,
0x1FEC,
0x1FF2,
0x1FF4,
0x1FF6,
0x1FFC,
0x200E,
0x200E,
0x2071,
0x2071,
0x207F,
0x207F,
0x2102,
0x2102,
0x2107,
0x2107,
0x210A,
0x2113,
0x2115,
0x2115,
0x2119,
0x211D,
0x2124,
0x2124,
0x2126,
0x2126,
0x2128,
0x2128,
0x212A,
0x212D,
0x212F,
0x2131,
0x2133,
0x2139,
0x213D,
0x213F,
0x2145,
0x2149,
0x2160,
0x2183,
0x2336,
0x237A,
0x2395,
0x2395,
0x249C,
0x24E9,
0x3005,
0x3007,
0x3021,
0x3029,
0x3031,
0x3035,
0x3038,
0x303C,
0x3041,
0x3096,
0x309D,
0x309F,
0x30A1,
0x30FA,
0x30FC,
0x30FF,
0x3105,
0x312C,
0x3131,
0x318E,
0x3190,
0x31B7,
0x31F0,
0x321C,
0x3220,
0x3243,
0x3260,
0x327B,
0x327F,
0x32B0,
0x32C0,
0x32CB,
0x32D0,
0x32FE,
0x3300,
0x3376,
0x337B,
0x33DD,
0x33E0,
0x33FE,
0x3400,
0x4DB5,
0x4E00,
0x9FA5,
0xA000,
0xA48C,
0xAC00,
0xD7A3,
0xD800,
0xFA2D,
0xFA30,
0xFA6A,
0xFB00,
0xFB06,
0xFB13,
0xFB17,
0xFF21,
0xFF3A,
0xFF41,
0xFF5A,
0xFF66,
0xFFBE,
0xFFC2,
0xFFC7,
0xFFCA,
0xFFCF,
0xFFD2,
0xFFD7,
0xFFDA,
0xFFDC,
0x10300,
0x1031E,
0x10320,
0x10323,
0x10330,
0x1034A,
0x10400,
0x10425,
0x10428,
0x1044D,
0x1D000,
0x1D0F5,
0x1D100,
0x1D126,
0x1D12A,
0x1D166,
0x1D16A,
0x1D172,
0x1D183,
0x1D184,
0x1D18C,
0x1D1A9,
0x1D1AE,
0x1D1DD,
0x1D400,
0x1D454,
0x1D456,
0x1D49C,
0x1D49E,
0x1D49F,
0x1D4A2,
0x1D4A2,
0x1D4A5,
0x1D4A6,
0x1D4A9,
0x1D4AC,
0x1D4AE,
0x1D4B9,
0x1D4BB,
0x1D4BB,
0x1D4BD,
0x1D4C0,
0x1D4C2,
0x1D4C3,
0x1D4C5,
0x1D505,
0x1D507,
0x1D50A,
0x1D50D,
0x1D514,
0x1D516,
0x1D51C,
0x1D51E,
0x1D539,
0x1D53B,
0x1D53E,
0x1D540,
0x1D544,
0x1D546,
0x1D546,
0x1D54A,
0x1D550,
0x1D552,
0x1D6A3,
0x1D6A8,
0x1D7C9,
0x20000,
0x2A6D6,
0x2F800,
0x2FA1D,
0xF0000,
0xFFFFD,
0x100000,
0x10FFFD,});
/** rfc4013 2.3. Prohibited Output */
static final CharClass saslProhibited = CharClass.fromClasses(C12, C21, C22, C3, C4, C5, C6, C7, C8, C9);
/** A prohibited string has been passed to StringPrep. */
public abstract static class StringPrepError extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
protected StringPrepError(String message) {
super(message);
}
}
/** A prohibited character was detected. */
@SuppressWarnings({"WeakerAccess", "JavaDoc"})
public static class StringPrepProhibitedCharacter extends StringPrepError {
/**
*
*/
private static final long serialVersionUID = 1L;
StringPrepProhibitedCharacter() {
super("String contains a prohibited character");
}
protected StringPrepProhibitedCharacter(String s) {
super(s);
}
}
/** A prohibited unassigned codepoint was detected. */
@SuppressWarnings("JavaDoc")
public static class StringPrepUnassignedCodepoint extends StringPrepProhibitedCharacter {
/**
*
*/
private static final long serialVersionUID = 1L;
StringPrepUnassignedCodepoint() {
super("String contains an unassigned codepoint");
}
}
/** RTL verification has failed, according to rfc3454 section 6. */
@SuppressWarnings({"unused", "JavaDoc"})
public static class StringPrepRTLError extends StringPrepError {
/**
*
*/
private static final long serialVersionUID = 1L;
StringPrepRTLError() {
super("Invalid RTL string");
}
}
public static class StringPrepRTLErrorBothRALandL extends StringPrepRTLError {
/**
*
*/
private static final long serialVersionUID = 1L;
}
public static class StringPrepRTLErrorRALWithoutPrefix extends StringPrepRTLError {
/**
*
*/
private static final long serialVersionUID = 1L;
}
public static class StringPrepRTLErrorRALWithoutSuffix extends StringPrepRTLError {
/**
*
*/
private static final long serialVersionUID = 1L;
}
/**
* Replace each character of {@code s} which is in the {@link CharClass} mapFrom with the string
* {@code mapTo}.
*/
static String applyMapTo(String s, CharClass mapFrom, String mapTo) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length();) {
int c = Character.codePointAt(s, i);
int charCount = Character.charCount(c);
if (mapFrom.isCharInClass(c))
result.append(mapTo);
else
result.append(s, i, i + charCount);
i += charCount;
}
return result.toString();
}
/**
* Return the first character index in s which is in {@link CharClass}, or -1 if no character is
* in the class.
*/
static int containsCharacterInClass(String s, CharClass charClass) {
for (int i = 0; i < s.length();) {
int c = Character.codePointAt(s, i);
if (charClass.isCharInClass(c))
return i;
i += Character.charCount(c);
}
return -1;
}
/**
* Perform RTL verification according to rfc3454 section 6. On failure, throw a subclass of
* {@link StringPrepRTLError}.
*/
protected static void verifyRTL(String s) throws StringPrepRTLError {
int containsRAL = containsCharacterInClass(s, D1);
if (containsRAL != -1) {
// 2) If a string contains any RandALCat character, the string MUST NOT
// contain any LCat character.
int containsL = containsCharacterInClass(s, D2);
if (containsL != -1)
throw new StringPrepRTLErrorBothRALandL();
// 3) If a string contains any RandALCat character, a RandALCat
// character MUST be the first character of the string
if (containsRAL != 0)
throw new StringPrepRTLErrorRALWithoutPrefix();
// ... and a RandALCat character MUST be the last character of the string.
if (!D1.isCharInClass(s.charAt(s.length() - 1)))
throw new StringPrepRTLErrorRALWithoutSuffix();
}
}
/** Apply SASLPrep and return the result. {@code} is treated as a stored string. */
public static String prepAsStoredString(String s) throws StringPrepError {
s = prepAsQueryString(s);
// rfc3454: 7. Unassigned Code Points in Stringprep Profiles
// Stored strings using the profile MUST NOT contain any unassigned code points.
// rfc4013: 2.5. Unassigned Code Points
// This profile specifies the [StringPrep, A.1] table as its list of unassigned
// code points.
int containsUnassignedCodepoint = containsCharacterInClass(s, A1);
if (containsUnassignedCodepoint != -1)
throw new StringPrepUnassignedCodepoint();
return s;
}
/** Apply SASLPrep and return the result. {@code} is treated as a query string. */
public static String prepAsQueryString(String s) throws StringPrepError {
// 1) Map
// rfc4013: 2.1. Mapping
// Note that applying the mapping this way works here because we only
// map to nothing or space. A StringPrep mapping that maps strings to
// another string (eg. case folding) can't be applied sequentially like
// this.
s = applyMapTo(s, B1, "");
s = applyMapTo(s, C12, " ");
// 2) Normalize
// rfc4013: 2.2. Normalization
s = Normalizer.normalize(s, Normalizer.Form.NFKC);
// 3) Prohibit
int idx = containsCharacterInClass(s, saslProhibited);
if (idx != -1)
throw new StringPrepProhibitedCharacter();
// 4) Check bidi
verifyRTL(s);
return s;
}
public static boolean isContainingProhibitedCharacters(String s) {
int idx = containsCharacterInClass(s, saslProhibited);
return idx != -1;
}
}