Woo! At this rate we'll have no open issues by the end of the week! :)

On Tue, Mar 11, 2008 at 3:44 AM, <[EMAIL PROTECTED]> wrote:

> Author: lindner
> Date: Tue Mar 11 03:44:17 2008
> New Revision: 635874
>
> URL: http://svn.apache.org/viewvc?rev=635874&view=rev
> Log:
> Commit crypto functions provided by Brian Eaton for SHINDIG-92
>
> Added:
>
>  
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypterException.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobExpiredException.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/Crypto.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/TimeSource.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/FakeTimeSource.java
> Modified:
>    incubator/shindig/trunk/java/gadgets/pom.xml
>
> Modified: incubator/shindig/trunk/java/gadgets/pom.xml
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/pom.xml?rev=635874&r1=635873&r2=635874&view=diff
>
> ==============================================================================
> --- incubator/shindig/trunk/java/gadgets/pom.xml (original)
> +++ incubator/shindig/trunk/java/gadgets/pom.xml Tue Mar 11 03:44:17 2008
> @@ -236,5 +236,10 @@
>       <version>2.3</version>
>       <scope>compile</scope>
>     </dependency>
> +    <dependency>
> +      <groupId>commons-codec</groupId>
> +      <artifactId>commons-codec</artifactId>
> +      <version>1.3</version>
> +    </dependency>
>   </dependencies>
>  </project>
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypter.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,200 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +import org.apache.commons.codec.binary.Base64;
> +import org.apache.commons.codec.digest.DigestUtils;
> +
> +import java.io.UnsupportedEncodingException;
> +import java.net.URLEncoder;
> +import java.security.GeneralSecurityException;
> +import java.util.HashMap;
> +import java.util.Iterator;
> +import java.util.Map;
> +
> +/**
> + * Utility class for managing signed, encrypted, and time stamped blobs.
> + * Blobs are made up of name/value pairs.  Time stamps are automatically
> + * included under BlobCrypter.TIMESTAMP_KEY.
> + */
> +public class BlobCrypter {
> +
> +  // Labels for key derivation
> +  private static final byte CIPHER_KEY_LABEL = 0;
> +  private static final byte HMAC_KEY_LABEL = 1;
> +
> +  /** Key used for time stamp (in seconds) of data */
> +  public static final String TIMESTAMP_KEY = "t";
> +
> +  /** minimum length of master key */
> +  public static final int MASTER_KEY_MIN_LEN = 16;
> +
> +  /** allow three minutes for clock skew */
> +  private static final long CLOCK_SKEW_ALLOWANCE = 180;
> +
> +  private static final String UTF8 = "UTF-8";
> +
> +  public TimeSource timeSource = new TimeSource();
> +  private byte[] cipherKey;
> +  private byte[] hmacKey;
> +
> +  /**
> +   * Builds a BlobCrypter from the specified master key
> +   *
> +   * @param masterKey
> +   */
> +  public BlobCrypter(byte[] masterKey) {
> +    if (masterKey.length < MASTER_KEY_MIN_LEN) {
> +      throw new IllegalArgumentException("Master key needs at least " +
> +          MASTER_KEY_MIN_LEN + " bytes");
> +    }
> +    cipherKey = deriveKey(CIPHER_KEY_LABEL, masterKey,
> Crypto.CIPHER_KEY_LEN);
> +    hmacKey = deriveKey(HMAC_KEY_LABEL, masterKey, 0);
> +  }
> +
> +  /**
> +   * Generates unique keys from a master key.
> +   *
> +   * @param label type of key to derive
> +   * @param masterKey master key
> +   * @param len length of key needed, less than 20 bytes.  20 bytes are
> returned
> +   * if len is 0.
> +   *
> +   * @return a derived key of the specified length
> +   */
> +  private byte[] deriveKey(byte label, byte[] masterKey, int len) {
> +    byte[] base = Crypto.concat(new byte[] { label }, masterKey);
> +    byte[] hash = DigestUtils.sha(base);
> +    if (len == 0) {
> +      return hash;
> +    }
> +    byte[] out = new byte[len];
> +    System.arraycopy(hash, 0, out, 0, out.length);
> +    return out;
> +  }
> +
> +  /**
> +   * Time stamps, encrypts, and signs a blob.
> +   *
> +   * @param in name/value pairs to encrypt
> +   * @return a base64 encoded blob
> +   *
> +   * @throws BlobCrypterException
> +   */
> +  public String wrap(Map<String, String> in)
> +  throws BlobCrypterException {
> +    if (in.containsKey(TIMESTAMP_KEY)) {
> +      throw new IllegalArgumentException("No 't' keys allowed for
> BlobCrypter");
> +    }
> +    try {
> +      byte[] encoded = serializeAndTimestamp(in);
> +      byte[] cipherText = Crypto.aes128cbcEncrypt(cipherKey, encoded);
> +      byte[] hmac = Crypto.hmacSha1(hmacKey, cipherText);
> +      byte[] b64 = Base64.encodeBase64(Crypto.concat(cipherText, hmac));
> +      return new String(b64, UTF8);
> +    } catch (UnsupportedEncodingException e) {
> +      throw new BlobCrypterException(e);
> +    } catch (GeneralSecurityException e) {
> +      throw new BlobCrypterException(e);
> +    }
> +  }
> +
> +  /**
> +   * Encode the input for transfer.  We use something a lot like HTML
> form
> +   * encodings.  The time stamp is in seconds since the epoch.
> +   */
> +  private byte[] serializeAndTimestamp(Map<String, String> in)
> +  throws UnsupportedEncodingException {
> +    StringBuffer sb = new StringBuffer();
> +    Iterator<Map.Entry<String,String>> vals = in.entrySet().iterator();
> +
> +    while (vals.hasNext()) {
> +      Map.Entry<String, String> val = vals.next();
> +      sb.append(URLEncoder.encode(val.getKey(), UTF8));
> +      sb.append("=");
> +      sb.append(URLEncoder.encode(val.getValue(), UTF8));
> +      sb.append("&");
> +    }
> +    sb.append(TIMESTAMP_KEY);
> +    sb.append("=");
> +    sb.append(timeSource.currentTimeMillis()/1000);
> +    return sb.toString().getBytes(UTF8);
> +  }
> +
> +  /**
> +   * Unwraps a blob.
> +   *
> +   * @param in blob
> +   * @param maxAgeSec maximum age for the blob
> +   * @return the name/value pairs, including the origin timestamp.
> +   *
> +   * @throws BlobExpiredException if the blob is too old to be accepted.
> +   * @throws BlobCrypterException if the blob can't be decoded.
> +   */
> +  public Map<String, String> unwrap(String in, int maxAgeSec)
> +  throws BlobCrypterException {
> +    try {
> +      byte[] bin = Base64.decodeBase64(in.getBytes());
> +      byte[] hmac = new byte[Crypto.HMAC_SHA1_LEN];
> +      byte[] cipherText = new byte[bin.length-Crypto.HMAC_SHA1_LEN];
> +      System.arraycopy(bin, 0, cipherText, 0, cipherText.length);
> +      System.arraycopy(bin, cipherText.length, hmac, 0, hmac.length);
> +      Crypto.hmacSha1Verify(hmacKey, cipherText, hmac);
> +      byte[] plain = Crypto.aes128cbcDecrypt(cipherKey, cipherText);
> +      Map<String, String> out = deserialize(plain);
> +      checkTimestamp(out, maxAgeSec);
> +      return out;
> +    } catch (GeneralSecurityException e) {
> +      throw new BlobCrypterException("Invalid token signature", e);
> +    } catch (ArrayIndexOutOfBoundsException e) {
> +      throw new BlobCrypterException("Invalid token format", e);
> +    } catch (UnsupportedEncodingException e) {
> +      throw new BlobCrypterException(e);
> +    }
> +
> +  }
> +
> +  private Map<String, String> deserialize(byte[] plain) throws
> UnsupportedEncodingException {
> +    String base = new String(plain, UTF8);
> +    String[] items = base.split("[&=]");
> +    Map<String, String> map = new HashMap<String, String>();
> +    for (int i=0; i < items.length; ) {
> +      String key = items[i++];
> +      String val = items[i++];
> +      map.put(key, val);
> +    }
> +    return map;
> +  }
> +
> +  /**
> +   * We allow a few minutes on either side of the validity window to
> account
> +   * for clock skew.
> +   */
> +  private void checkTimestamp(Map<String, String> out, int maxAge)
> +  throws BlobExpiredException {
> +    long origin = Long.parseLong(out.get(TIMESTAMP_KEY));
> +    long minTime = origin - CLOCK_SKEW_ALLOWANCE;
> +    long maxTime = origin + maxAge + CLOCK_SKEW_ALLOWANCE;
> +    long now = timeSource.currentTimeMillis()/1000;
> +    if (!(minTime < now && now < maxTime)) {
> +      throw new BlobExpiredException(minTime, now, maxTime);
> +    }
> +  }
> +
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypterException.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypterException.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypterException.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobCrypterException.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,36 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +/**
> + * For all exceptions thrown by BlobCrypter
> + */
> +public class BlobCrypterException extends Exception {
> +  public BlobCrypterException(Throwable cause) {
> +    super(cause);
> +  }
> +
> +  public BlobCrypterException(String msg, Throwable cause) {
> +    super(msg, cause);
> +  }
> +
> +  protected BlobCrypterException(String msg) {
> +    super(msg);
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobExpiredException.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobExpiredException.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobExpiredException.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/BlobExpiredException.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,44 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +import java.util.Date;
> +
> +/**
> + * Thrown when a blob has expired.
> + */
> +public class BlobExpiredException extends BlobCrypterException {
> +
> +  public final Date minDate;
> +  public final Date used;
> +  public final Date maxDate;
> +
> +  public BlobExpiredException(long minTime, long now, long maxTime) {
> +    this(new Date(minTime*1000), new Date(now*1000), new
> Date(maxTime*1000));
> +  }
> +
> +  public BlobExpiredException(Date minTime, Date now, Date maxTime) {
> +    super("Blob expired, was valid from " + minTime + " to " + maxTime
> +        + ", attempted use at " + now);
> +    this.minDate = minTime;
> +    this.used = now;
> +    this.maxDate = maxTime;
> +  }
> +
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/Crypto.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/Crypto.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/Crypto.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/Crypto.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,215 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +import org.apache.commons.codec.binary.Hex;
> +
> +import java.security.GeneralSecurityException;
> +import java.security.Key;
> +import java.security.SecureRandom;
> +
> +import javax.crypto.Cipher;
> +import javax.crypto.Mac;
> +import javax.crypto.spec.IvParameterSpec;
> +import javax.crypto.spec.SecretKeySpec;
> +
> +/**
> + * Cryptographic utility functions.
> + */
> +public class Crypto {
> +
> +  /**
> +   * Use this random number generator instead of creating your own.  This
> is
> +   * thread-safe.
> +   */
> +  public static SecureRandom rand = new SecureRandom();
> +
> +  /**
> +   * HMAC algorithm to use
> +   */
> +  private final static String HMAC_TYPE = "HMACSHA1";
> +
> +  /**
> +   * minimum safe length for hmac keys (this is good practice, but not
> +   * actually a requirement of the algorithm
> +   */
> +  private final static int MIN_HMAC_KEY_LEN = 8;
> +
> +  /**
> +   * Encryption algorithm to use
> +   */
> +  private final static String CIPHER_TYPE = "AES/CBC/PKCS5Padding";
> +
> +  private final static String CIPHER_KEY_TYPE = "AES";
> +
> +  /**
> +   * Use keys of this length for encryption operations
> +   */
> +  public final static int CIPHER_KEY_LEN = 16;
> +
> +  private static int CIPHER_BLOCK_SIZE = 16;
> +
> +  /**
> +   * Length of HMAC SHA1 output
> +   */
> +  public final static int HMAC_SHA1_LEN = 20;
> +
> +  // everything is static, no instantiating this class
> +  private Crypto() {
> +  }
> +
> +  /**
> +   * Gets a hex encoded random string.
> +   *
> +   * @param numBytes number of bytes of randomness.
> +   */
> +  public static String getRandomString(int numBytes) {
> +    return new String(Hex.encodeHex(getRandomBytes(numBytes)));
> +  }
> +
> +  /**
> +   * Returns strong random bytes.
> +   *
> +   * @param numBytes number of bytes of randomness
> +   */
> +  public static byte[] getRandomBytes(int numBytes) {
> +    byte[] out = new byte[numBytes];
> +    rand.nextBytes(out);
> +    return out;
> +  }
> +
> +  /**
> +   * HMAC sha1
> +   *
> +   * @param key the key must be at least 8 bytes in length.
> +   * @param in byte array to HMAC.
> +   * @return the hash
> +   *
> +   * @throws GeneralSecurityException
> +   */
> +  public static byte[] hmacSha1(byte[] key, byte[] in) throws
> GeneralSecurityException {
> +    if (key.length < MIN_HMAC_KEY_LEN) {
> +      throw new GeneralSecurityException("HMAC key should be at least "
> +          + MIN_HMAC_KEY_LEN + " bytes.");
> +    }
> +    Mac hmac = Mac.getInstance(HMAC_TYPE);
> +    Key hmacKey = new SecretKeySpec(key, HMAC_TYPE);
> +    hmac.init(hmacKey);
> +    hmac.update(in);
> +    return hmac.doFinal();
> +  }
> +
> +  /**
> +   * Verifies an HMAC SHA1 hash.  Throws if the verification fails.
> +   *
> +   * @param key
> +   * @param in
> +   * @param expected
> +   * @throws GeneralSecurityException
> +   */
> +  public static void hmacSha1Verify(byte[] key, byte[] in, byte[]
> expected)
> +  throws GeneralSecurityException {
> +    Mac hmac = Mac.getInstance(HMAC_TYPE);
> +    Key hmacKey = new SecretKeySpec(key, HMAC_TYPE);
> +    hmac.init(hmacKey);
> +    hmac.update(in);
> +    byte actual[] = hmac.doFinal();
> +    if (actual.length != expected.length) {
> +      throw new GeneralSecurityException("HMAC verification failure");
> +    }
> +    for (int i=0; i < actual.length; i++) {
> +      if (actual[i] != expected[i]) {
> +        throw new GeneralSecurityException("HMAC verification failure");
> +      }
> +    }
> +  }
> +
> +  /**
> +   * AES-128-CBC encryption.  The IV is returned as the first 16 bytes
> +   * of the cipher text.
> +   *
> +   * @param key
> +   * @param plain
> +   *
> +   * @return the IV and cipher text
> +   *
> +   * @throws GeneralSecurityException
> +   */
> +  public static byte[] aes128cbcEncrypt(byte[] key, byte[] plain)
> +  throws GeneralSecurityException {
> +    Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
> +    Key cipherKey = new SecretKeySpec(key, CIPHER_KEY_TYPE);
> +    byte iv[] = getRandomBytes(cipher.getBlockSize());
> +    IvParameterSpec ivSpec = new IvParameterSpec(iv);
> +    cipher.init(Cipher.ENCRYPT_MODE, cipherKey, ivSpec);
> +    byte[] cipherText = cipher.doFinal(plain);
> +    return concat(iv, cipherText);
> +  }
> +
> +  /**
> +   * AES-128-CBC decryption.  The IV is assumed to be the first 16 bytes
> +   * of the cipher text.
> +   *
> +   * @param key
> +   * @param cipherText
> +   *
> +   * @return the plain text
> +   *
> +   * @throws GeneralSecurityException
> +   */
> +  public static byte[] aes128cbcDecrypt(byte[] key, byte[] cipherText)
> +  throws GeneralSecurityException {
> +    byte iv[] = new byte[CIPHER_BLOCK_SIZE];
> +    System.arraycopy(cipherText, 0, iv, 0, iv.length);
> +    return aes128cbcDecryptWithIv(key, iv, cipherText, iv.length);
> +  }
> +
> +  /**
> +   * AES-128-CBC decryption with a particular IV.
> +   *
> +   * @param key decryption key
> +   * @param iv initial vector for decryption
> +   * @param cipherText cipher text to decrypt
> +   * @param offset offset into cipher text to begin decryption
> +   *
> +   * @return the plain text
> +   *
> +   * @throws GeneralSecurityException
> +   */
> +  public static byte[] aes128cbcDecryptWithIv(byte[] key, byte[] iv,
> +      byte[] cipherText, int offset) throws GeneralSecurityException {
> +    Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
> +    Key cipherKey = new SecretKeySpec(key, CIPHER_KEY_TYPE);
> +    IvParameterSpec ivSpec = new IvParameterSpec(iv);
> +    cipher.init(Cipher.DECRYPT_MODE, cipherKey, ivSpec);
> +    return cipher.doFinal(cipherText, offset, cipherText.length-offset);
> +  }
> +
> +  /**
> +   * Concatenate two byte arrays.
> +   */
> +  public static byte[] concat(byte[] a, byte[] b) {
> +    byte[] out = new byte[a.length + b.length];
> +    int cursor = 0;
> +    System.arraycopy(a, 0, out, cursor, a.length);
> +    cursor += a.length;
> +    System.arraycopy(b, 0, out, cursor, b.length);
> +    return out;
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/TimeSource.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/TimeSource.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/TimeSource.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/TimeSource.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,29 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +/**
> + * Simple source of current time to use for dependency injection.
> + */
> +public class TimeSource {
> +
> +  public long currentTimeMillis() {
> +    return System.currentTimeMillis();
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/BlobCrypterTest.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,180 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +import junit.framework.JUnit4TestAdapter;
> +
> +import static org.junit.Assert.*;
> +
> +import org.apache.commons.codec.binary.Base64;
> +import org.junit.Test;
> +
> +import java.util.HashMap;
> +import java.util.Map;
> +
> +public class BlobCrypterTest {
> +  public static junit.framework.Test suite() {
> +    return new JUnit4TestAdapter(BlobCrypterTest.class);
> +  }
> +
> +  private BlobCrypter crypter;
> +  private FakeTimeSource timeSource;
> +
> +  public BlobCrypterTest() {
> +    crypter = new BlobCrypter("0123456789abcdef".getBytes());
> +    timeSource = new FakeTimeSource();
> +    crypter.timeSource = timeSource;
> +  }
> +
> +  @Test
> +  public void testEncryptAndDecrypt() throws Exception {
> +    checkString("");
> +    checkString("a");
> +    checkString("ab");
> +    checkString("dfkljdasklsdfklasdjfklajsdfkljasdklfjasdkljfaskldjf");
> +    checkString(Crypto.getRandomString(500));
> +  }
> +
> +  private void checkString(String string) throws Exception {
> +    Map<String, String> in = new HashMap<String, String>();
> +    if (string != null) {
> +      in.put("a", string);
> +    }
> +    String blob = crypter.wrap(in);
> +    Map<String, String> out = crypter.unwrap(blob, 0);
> +    assertEquals(string, out.get("a"));
> +  }
> +
> +  @Test
> +  public void testManyEntries() throws Exception {
> +    Map<String, String> in = new HashMap<String, String>();
> +    for (int i=0; i < 1000; i++) {
> +      in.put(Integer.toString(i), Integer.toString(i));
> +    }
> +    String blob = crypter.wrap(in);
> +    Map<String, String> out = crypter.unwrap(blob, 0);
> +    for (int i=0; i < 1000; i++) {
> +      assertEquals(out.get(Integer.toString(i)), Integer.toString(i));
> +    }
> +  }
> +
> +  @Test
> +  public void testTimeStamping() throws Exception {
> +    long start = 1201917724000L;
> +    long skew = 180000;
> +    int maxAge = 300; // 5 minutes
> +    int realAge = 600; // 10 minutes
> +    try {
> +
> +      timeSource.setCurrentTimeMillis(start);
> +      Map<String, String> in = new HashMap<String, String>();
> +      in.put("a", "b");
> +      String blob = crypter.wrap(in);
> +      timeSource.incrementSeconds(realAge);
> +      crypter.unwrap(blob, maxAge);
> +      fail("Blob should have expired");
> +    } catch (BlobExpiredException e) {
> +      assertEquals(start-skew, e.minDate.getTime());
> +      assertEquals(start+realAge*1000, e.used.getTime());
> +      assertEquals(start+skew+maxAge*1000, e.maxDate.getTime());
> +    }
> +  }
> +
> +  @Test
> +  public void testTamperIV() throws Exception {
> +    try {
> +      Map<String, String> in = new HashMap<String, String>();
> +      in.put("a", "b");
> +      String blob = crypter.wrap(in);
> +      byte[] blobBytes = Base64.decodeBase64(blob.getBytes());
> +      blobBytes[0] ^= 0x01;
> +      String tampered = new String(Base64.encodeBase64(blobBytes));
> +      crypter.unwrap(tampered, 30);
> +      fail("Signature verification should have failed.");
> +    } catch (BlobCrypterException e) {
> +      // Good
> +    }
> +  }
> +
> +  @Test
> +  public void testTamperData() throws Exception {
> +    try {
> +      Map<String, String> in = new HashMap<String, String>();
> +      in.put("a", "b");
> +      String blob = crypter.wrap(in);
> +      byte[] blobBytes = Base64.decodeBase64(blob.getBytes());
> +      blobBytes[30] ^= 0x01;
> +      String tampered = new String(Base64.encodeBase64(blobBytes));
> +      crypter.unwrap(tampered, 30);
> +      fail("Signature verification should have failed.");
> +    } catch (BlobCrypterException e) {
> +      // Good
> +    }
> +  }
> +
> +  @Test
> +  public void testTamperMac() throws Exception {
> +    try {
> +      Map<String, String> in = new HashMap<String, String>();
> +      in.put("a", "b");
> +      String blob = crypter.wrap(in);
> +      byte[] blobBytes = Base64.decodeBase64(blob.getBytes());
> +      blobBytes[blobBytes.length-1] ^= 0x01;
> +      String tampered = new String(Base64.encodeBase64(blobBytes));
> +      crypter.unwrap(tampered, 30);
> +      fail("Signature verification should have failed.");
> +    } catch (BlobCrypterException e) {
> +      // Good
> +    }
> +  }
> +
> +  @Test
> +  public void testFixedKey() throws Exception {
> +    BlobCrypter alt = new BlobCrypter("0123456789abcdef".getBytes());
> +    Map<String, String> in = new HashMap<String, String>();
> +    in.put("a", "b");
> +    String blob = crypter.wrap(in);
> +    Map<String, String> out = alt.unwrap(blob, 30);
> +    assertEquals("b", out.get("a"));
> +  }
> +
> +  @Test
> +  public void testBadKey() throws Exception {
> +    BlobCrypter alt = new BlobCrypter("1123456789abcdef".getBytes());
> +    Map<String, String> in = new HashMap<String, String>();
> +    in.put("a", "b");
> +    String blob = crypter.wrap(in);
> +    try {
> +      Map<String, String> out = alt.unwrap(blob, 30);
> +      fail("Decryption should have failed");
> +    } catch (BlobCrypterException e) {
> +      // Good.
> +    }
> +  }
> +
> +  @Test
> +  public void testShortKeyFails() throws Exception {
> +    try {
> +      new BlobCrypter("0123456789abcde".getBytes());
> +      fail("Short key should fail");
> +    } catch (IllegalArgumentException e) {
> +      // good.
> +    }
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/CryptoTest.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,94 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +import static org.junit.Assert.*;
> +
> +import junit.framework.JUnit4TestAdapter;
> +
> +import org.junit.Test;
> +
> +import java.security.GeneralSecurityException;
> +
> +public class CryptoTest {
> +  public static junit.framework.Test suite() {
> +    return new JUnit4TestAdapter(CryptoTest.class);
> +  }
> +
> +  private BlobCrypter crypter;
> +
> +  public CryptoTest() {
> +    crypter = new BlobCrypter("0123456789abcdef".getBytes());
> +    crypter.timeSource = new FakeTimeSource();
> +  }
> +
> +  @Test
> +  public void testHmacSha1() throws Exception {
> +    String key = "abcd1234";
> +    String val = "your mother is a hedgehog";
> +    byte[] expected = new byte[] {
> +        -21, 2, 47, -101, 9, -40, 18, 43, 76, 117,
> +        -51, 115, -122, -91, 39, 26, -18, 122, 30, 90,
> +    };
> +    byte[] hmac = Crypto.hmacSha1(key.getBytes(), val.getBytes());
> +    assertArrayEquals(expected, hmac);
> +  }
> +
> +  @Test
> +  public void testHmacSha1Verify() throws Exception {
> +    String key = "abcd1234";
> +    String val = "your mother is a hedgehog";
> +    byte[] expected = new byte[] {
> +        -21, 2, 47, -101, 9, -40, 18, 43, 76, 117,
> +        -51, 115, -122, -91, 39, 26, -18, 122, 30, 90,
> +    };
> +    Crypto.hmacSha1Verify(key.getBytes(), val.getBytes(), expected);
> +  }
> +
> +
> +  @Test
> +  public void testHmacSha1VerifyTampered() throws Exception {
> +    String key = "abcd1234";
> +    String val = "your mother is a hedgehog";
> +    byte[] expected = new byte[] {
> +        -21, 2, 47, -101, 9, -40, 18, 43, 76, 117,
> +        -51, 115, -122, -91, 39, 0, -18, 122, 30, 90,
> +    };
> +    try {
> +      Crypto.hmacSha1Verify(key.getBytes(), val.getBytes(), expected);
> +      fail();
> +    } catch (GeneralSecurityException e) {
> +      // OK
> +    }
> +  }
> +
> +  @Test
> +  public void testAes128Cbc() throws Exception {
> +    byte[] key = Crypto.getRandomBytes(Crypto.CIPHER_KEY_LEN);
> +    for (byte i=0; i < 50; i++) {
> +      byte[] orig = new byte[i];
> +      for (byte j=0; j < i; j++) {
> +        orig[j] = j;
> +      }
> +      byte[] cipherText = Crypto.aes128cbcEncrypt(key, orig);
> +      byte[] plainText = Crypto.aes128cbcDecrypt(key, cipherText);
> +      assertArrayEquals("Array of length " + i, orig, plainText);
> +    }
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/FakeTimeSource.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/FakeTimeSource.java?rev=635874&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/FakeTimeSource.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/util/FakeTimeSource.java
> Tue Mar 11 03:44:17 2008
> @@ -0,0 +1,48 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.shindig.util;
> +
> +/**
> + * Fake time source for dependency injection.
> + */
> +public class FakeTimeSource extends TimeSource {
> +
> +  public long now;
> +
> +  public FakeTimeSource() {
> +    this(System.currentTimeMillis());
> +  }
> +
> +  public FakeTimeSource(long now) {
> +    this.now = now;
> +  }
> +
> +  @Override
> +  public long currentTimeMillis() {
> +    return now;
> +  }
> +
> +  public void setCurrentTimeMillis(long now) {
> +    this.now = now;
> +  }
> +
> +  public void incrementSeconds(int seconds) {
> +    now += seconds*1000;
> +  }
> +}
>
>
>


-- 
~Kevin

Reply via email to