package org.batfish.common.util; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.batfish.common.BatfishException; /** * This class is a derivative work based on the perl module Crypt-Juniper * <http://search.cpan.org/~kbrint/Crypt-Juniper/lib/Crypt/Juniper.pm> by Kevin * Brintnall * * Permission to release this derived code under the Apache 2.0 License was * granted by Kevin Brintnall on 2015/12/04 */ public final class JuniperUtils { private static final JuniperUtils INSTANCE = new JuniperUtils(); public static final String SALT = "'rn5;/Jq_g,ROS-ObQ.l+h)*+(Amq?]Wn75zq3eN6kI}VZon03etYA>}{&{[~|)/}l}QXAgpG_wnv[w~JG?|(LfWe88#omyZv.PcOSDU5j2_9C5p9kZ7`UiHry67%<;mHO*Mz=Mn/G-R%(=Onc6A^ps^28%4xAU*i5&/Y<3L/v-dzI0dtjw=[p1=[pn[Z_8W~uW5K{9{HOCKwi6@6DfrG?Ds*HZ!ai,a$+LzpC/,bvp2:}t4<Ol3sE|I&|6'<NNZ}SE#voQd9*[yZjrnP~?1R^+Un%oOee!D#7}<=QEJ<@*&IRpC,5LH+OKVg,?^M4IAq9x8tvcRC]g7KG:U?Q@PvD[Od's|.`sH1#/IEzMoDu+k`1DnW+XsAt3?*n1=z:W&O!VQ--:U&wF]&Bj4O(51NsSU4{I0%3$}bi3_[_[y<A}Zbil%Rd^Q{)4F_lIoNl.AVyEM)O<d%pD+Q~X&:P0CyTcAfav[|zE;Q$x%ib^oS_epZ20)l.w&ZaBJHTE1D^dM$/w~Zs6[ED2K++PRF1z/ENxaL3^?}tvYWoFRez~dY2E~uk[Y=~7T7E&ME)$LA/%0g'W{oA3j9Q-'gV1YB*)'.fNdIPr'krar)}k[md:V5x]rhze(8?1jEWJJBICH!At<U-:{XSpRCSa_S7d<Pv(oW_Zu^&#UuI6BjydC>ZkDK8_W;zbBJ9T'<:`<!~JL|z4b2o4t'.M??:nU/QF'F0U0H,KO2L8xPL|z`%8w-v,,js/8`Bkg8TR6S<x)z3x,du31n=_0-|xU$0;5/A8zjdJ19hYYOw-g8c_L.9bh^/Ekv-6tHo{x=x!f0P/(P+~j0c]|LQ1kbU-VBs~PiJJXH1h^Bo{3It[Itar%g4b=Jd'YnKA_}okjW72Hy_I_#KnI^784F}0XD}8{W4*#Ceo#l!5qB`ozr._=/$}iEULC?$eB|1hlvZta-*[R31Njig~_c#FQS1N(Y5k{wgC.U1,y-+rx[~HN`Om_}-#vKeOIU'0T0/Y>glh/Fz<rc^cV2Lpi.%T."; public static String decryptAndHashJuniper9CipherText(String key) { String privateSecret = decryptJuniper9CipherText(key); String saltedSecret = privateSecret + SALT; String sha256Digest = CommonUtil.sha256Digest(saltedSecret); return sha256Digest; } public static String decryptJuniper9CipherText(String key) { return INSTANCE.decrypt(key); } private final char[] _allCharacters; private final Map<Character, Integer> _allCharactersIndexMap; private final String[] _characterFamilies; private final Map<Character, Integer> _characterFamilyReverseIndexMap; private final List<List<Integer>> _codeMatrix; private final Pattern _validationRegex; private JuniperUtils() { _codeMatrix = initCodeMatrix(); _characterFamilies = new String[] { "QzF3n6/9CAtpu0O", "B1IREhcSyrleKvMW8LXx", "7N-dVbwsY2g4oaJZGUDj", "iHkq.mPf5T" }; _characterFamilyReverseIndexMap = initCharacterFamilyReverseIndexMap(); _allCharacters = initAllCharacters(); _allCharactersIndexMap = initAllCharactersIndexMap(); _validationRegex = initValidationRegex(); } private String decrypt(String key) { boolean valid = _validationRegex.matcher(key).matches(); if (!valid) { throw new BatfishException( "Invalid Juniper $9$ ciphertext: \"" + key + "\""); } String[] chars = new String[] { key.substring("$9$".length()) }; char first = nibble(chars, 1).charAt(0); nibble(chars, _characterFamilyReverseIndexMap.get(first)); char prev = first; String decrypted = ""; while (chars[0].length() > 0) { List<Integer> decode = _codeMatrix .get(decrypted.length() % _codeMatrix.size()); int len = decode.size(); char[] nibbleChars = new char[len]; nibble(chars, len).getChars(0, len, nibbleChars, 0); List<Integer> gaps = new ArrayList<>(); for (int i = 0; i < len; i++) { char nibbleChar = nibbleChars[i]; int g = gap(prev, nibbleChar); prev = nibbleChar; gaps.add(g); } char newChar = gapDecode(gaps, decode); decrypted += newChar; } return decrypted; } private int gap(char a, char b) { int diff = _allCharactersIndexMap.get(b) - _allCharactersIndexMap.get(a); int positiveDiff = diff + _allCharacters.length; int ret = positiveDiff % _allCharacters.length - 1; return ret; } private char gapDecode(List<Integer> gaps, List<Integer> codeRow) { int num = 0; if (gaps.size() != codeRow.size()) { throw new BatfishException("Gaps size does not match codeRow size"); } for (int i = 0; i < gaps.size(); i++) { num += gaps.get(i) * codeRow.get(i); } int val = num % 256; char ret = (char) val; return ret; } private char[] initAllCharacters() { String allCharacters = String.join("", _characterFamilies); int len = allCharacters.length(); char[] numAlpha = new char[len]; allCharacters.getChars(0, len, numAlpha, 0); return numAlpha; } private Map<Character, Integer> initAllCharactersIndexMap() { Map<Character, Integer> alphaNum = new HashMap<>(); for (int i = 0; i < _allCharacters.length; i++) { alphaNum.put(_allCharacters[i], i); } return alphaNum; } private Map<Character, Integer> initCharacterFamilyReverseIndexMap() { Map<Character, Integer> extra = new HashMap<>(); for (int characterFamilyIndex = 0; characterFamilyIndex < _characterFamilies.length; characterFamilyIndex++) { String characterFamily = _characterFamilies[characterFamilyIndex]; for (int i = 0; i < characterFamily.length(); i++) { char c = characterFamily.charAt(i); extra.put(c, 3 - characterFamilyIndex); } } return extra; } private List<List<Integer>> initCodeMatrix() { List<List<Integer>> codeMatrix = new ArrayList<>(); codeMatrix.add(Arrays.asList(new Integer[] { 1, 4, 32 })); codeMatrix.add(Arrays.asList(new Integer[] { 1, 16, 32 })); codeMatrix.add(Arrays.asList(new Integer[] { 1, 8, 32 })); codeMatrix.add(Arrays.asList(new Integer[] { 1, 64 })); codeMatrix.add(Arrays.asList(new Integer[] { 1, 32 })); codeMatrix.add(Arrays.asList(new Integer[] { 1, 4, 16, 128 })); codeMatrix.add(Arrays.asList(new Integer[] { 1, 32, 64 })); return codeMatrix; } private Pattern initValidationRegex() { String allCharacters = String.join("", _characterFamilies); String allCharactersAdjusted = allCharacters.replaceAll("-", "") + "-"; String regexText = "^\\$9\\$[" + allCharactersAdjusted + "]{4,}$"; return Pattern.compile(regexText); } private String nibble(String[] chars, int length) { String nib = chars[0].substring(0, length); chars[0] = chars[0].substring(length); return nib; } }