/* * 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.common.crypto; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.apache.shindig.common.crypto.BasicBlobCrypter; import org.apache.shindig.common.crypto.BlobCrypter; import org.apache.shindig.common.crypto.BlobCrypterException; import org.apache.shindig.common.crypto.BlobExpiredException; import org.apache.shindig.common.crypto.Crypto; import org.apache.shindig.common.util.FakeTimeSource; 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 BasicBlobCrypter crypter; private FakeTimeSource timeSource; public BlobCrypterTest() { crypter = new BasicBlobCrypter("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)); checkString("foo bar baz"); checkString("foo\nbar\nbaz"); } private void checkString(String string) throws Exception { Map<String, String> in = Maps.newHashMap(); 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 testDecryptGarbage() throws Exception { StringBuilder sb = new StringBuilder(); for (int i=0; i < 100; ++i) { assertThrowsBlobCrypterException(sb.toString()); sb.append("a"); } } private void assertThrowsBlobCrypterException(String in) { try { crypter.unwrap(in, 1000); fail("Should have thrown BlobCrypterException for input " + in); } catch (BlobCrypterException e) { // Good. } } @Test public void testManyEntries() throws Exception { Map<String, String> in = Maps.newHashMap(); 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 = ImmutableMap.of("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 = ImmutableMap.of("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 = ImmutableMap.of("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 = ImmutableMap.of("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 BasicBlobCrypter("0123456789abcdef".getBytes()); Map<String, String> in = ImmutableMap.of("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 BasicBlobCrypter("1123456789abcdef".getBytes()); Map<String, String> in = ImmutableMap.of("a","b"); String blob = crypter.wrap(in); try { alt.unwrap(blob, 30); fail("Decryption should have failed"); } catch (BlobCrypterException e) { // Good. } } @Test public void testShortKeyFails() throws Exception { try { new BasicBlobCrypter("0123456789abcde".getBytes()); fail("Short key should fail"); } catch (IllegalArgumentException e) { // good. } } }