1b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallampackage org.bouncycastle.crypto.generators; 2b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 3b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.CipherParameters; 4b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.Digest; 5b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.ExtendedDigest; 6b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.PBEParametersGenerator; 7b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.params.KeyParameter; 8b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.params.ParametersWithIV; 9b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 10b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam/** 11b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0. 12b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * <p> 13b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * The document this implementation is based on can be found at 14b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html> 15b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * RSA's PKCS12 Page</a> 16b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 17b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallampublic class PKCS12ParametersGenerator 18b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam extends PBEParametersGenerator 19b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam{ 20b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public static final int KEY_MATERIAL = 1; 21b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public static final int IV_MATERIAL = 2; 22b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public static final int MAC_MATERIAL = 3; 23b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 24b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam private Digest digest; 25b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 26b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam private int u; 27b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam private int v; 28b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 29b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam /** 30b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * Construct a PKCS 12 Parameters generator. This constructor will 31b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * accept any digest which also implements ExtendedDigest. 32b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * 33b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @param digest the digest to be used as the source of derived keys. 34b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @exception IllegalArgumentException if an unknown digest is passed in. 35b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 36b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public PKCS12ParametersGenerator( 37b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam Digest digest) 38b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 39b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam this.digest = digest; 40b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam if (digest instanceof ExtendedDigest) 41b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 42b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam u = digest.getDigestSize(); 43b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam v = ((ExtendedDigest)digest).getByteLength(); 44b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 45b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam else 46b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 47b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported"); 48b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 49b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 50b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 51b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam /** 52b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * add a + b + 1, returning the result in a. The a value is treated 53b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * as a BigInteger of length (b.length * 8) bits. The result is 54b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * modulo 2^b.length in case of overflow. 55b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 56b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam private void adjust( 57b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] a, 58b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int aOff, 59b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] b) 60b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 61b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1; 62b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 63b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam a[aOff + b.length - 1] = (byte)x; 64b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam x >>>= 8; 65b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 66b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int i = b.length - 2; i >= 0; i--) 67b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 68b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam x += (b[i] & 0xff) + (a[aOff + i] & 0xff); 69b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam a[aOff + i] = (byte)x; 70b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam x >>>= 8; 71b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 72b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 73b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 74b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam /** 75b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * generation of a derived key ala PKCS12 V1.0. 76b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 77b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam private byte[] generateDerivedKey( 78b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int idByte, 79b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int n) 80b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 81b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] D = new byte[v]; 82b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] dKey = new byte[n]; 83b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 84b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int i = 0; i != D.length; i++) 85b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 86b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam D[i] = (byte)idByte; 87b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 88b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 89b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] S; 90b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 91b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam if ((salt != null) && (salt.length != 0)) 92b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 93b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam S = new byte[v * ((salt.length + v - 1) / v)]; 94b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 95b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int i = 0; i != S.length; i++) 96b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 97b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam S[i] = salt[i % salt.length]; 98b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 99b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 100b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam else 101b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 102b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam S = new byte[0]; 103b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 104b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 105b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] P; 106b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 107b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam if ((password != null) && (password.length != 0)) 108b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 109b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam P = new byte[v * ((password.length + v - 1) / v)]; 110b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 111b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int i = 0; i != P.length; i++) 112b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 113b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam P[i] = password[i % password.length]; 114b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 115b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 116b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam else 117b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 118b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam P = new byte[0]; 119b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 120b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 121b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] I = new byte[S.length + P.length]; 122b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 123b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam System.arraycopy(S, 0, I, 0, S.length); 124b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam System.arraycopy(P, 0, I, S.length, P.length); 125b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 126b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] B = new byte[v]; 127b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int c = (n + u - 1) / u; 128b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 129b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int i = 1; i <= c; i++) 130b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 131b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] A = new byte[u]; 132b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 133b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam digest.update(D, 0, D.length); 134b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam digest.update(I, 0, I.length); 135b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam digest.doFinal(A, 0); 1364c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom for (int j = 1; j < iterationCount; j++) 137b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 138b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam digest.update(A, 0, A.length); 139b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam digest.doFinal(A, 0); 140b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 141b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 142b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int j = 0; j != B.length; j++) 143b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 144b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam B[j] = A[j % A.length]; 145b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 146b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 147b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam for (int j = 0; j != I.length / v; j++) 148b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 149b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam adjust(I, j * v, B); 150b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 151b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 152b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam if (i == c) 153b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 154b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u)); 155b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 156b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam else 157b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 158b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam System.arraycopy(A, 0, dKey, (i - 1) * u, A.length); 159b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 160b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 161b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 162b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam return dKey; 163b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 164b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 165b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam /** 166b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * Generate a key parameter derived from the password, salt, and iteration 167b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * count we are currently initialised with. 168b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * 169b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @param keySize the size of the key we want (in bits) 170b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @return a KeyParameter object. 171b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 172b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public CipherParameters generateDerivedParameters( 173b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int keySize) 174b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 175b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam keySize = keySize / 8; 176b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 177b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); 178b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 179b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam return new KeyParameter(dKey, 0, keySize); 180b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 181b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 182b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam /** 183b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * Generate a key with initialisation vector parameter derived from 184b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * the password, salt, and iteration count we are currently initialised 185b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * with. 186b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * 187b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @param keySize the size of the key we want (in bits) 188b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @param ivSize the size of the iv we want (in bits) 189b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @return a ParametersWithIV object. 190b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 191b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public CipherParameters generateDerivedParameters( 192b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int keySize, 193b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int ivSize) 194b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 195b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam keySize = keySize / 8; 196b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam ivSize = ivSize / 8; 197b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 198b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); 199b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 200b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] iv = generateDerivedKey(IV_MATERIAL, ivSize); 201b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 202b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize); 203b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 204b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 205b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam /** 206b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * Generate a key parameter for use with a MAC derived from the password, 207b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * salt, and iteration count we are currently initialised with. 208b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * 209b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @param keySize the size of the key we want (in bits) 210b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * @return a KeyParameter object. 211b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */ 212b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam public CipherParameters generateDerivedMacParameters( 213b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam int keySize) 214b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam { 215b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam keySize = keySize / 8; 216b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 217b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam byte[] dKey = generateDerivedKey(MAC_MATERIAL, keySize); 218b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam 219b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam return new KeyParameter(dKey, 0, keySize); 220b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam } 221b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam} 222