/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.wildfly.security.sasl.digest._private;
import static org.junit.Assert.assertEquals;
import static org.wildfly.security.mechanism.digest.DigestUtil.userRealmPasswordDigest;
import static org.wildfly.security.sasl.digest._private.DigestUtil.H_A1;
import static org.wildfly.security.sasl.digest._private.DigestUtil.computeHMAC;
import static org.wildfly.security.sasl.digest._private.DigestUtil.convertToHexBytesWithLeftPadding;
import static org.wildfly.security.sasl.digest._private.DigestUtil.create3desSubKey;
import static org.wildfly.security.sasl.digest._private.DigestUtil.decodeByteOrderedInteger;
import static org.wildfly.security.sasl.digest._private.DigestUtil.digestResponse;
import static org.wildfly.security.sasl.digest._private.DigestUtil.integerByteOrdered;
import static org.wildfly.security.sasl.digest._private.DigestUtil.messageDigestAlgorithm;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Locale;
import javax.crypto.Mac;
import org.junit.Before;
import org.junit.Test;
import org.wildfly.security.sasl.util.SaslMechanismInformation;
import org.wildfly.security.util.ByteIterator;
import org.wildfly.security.util.CodePointIterator;
/**
* Digest SASL utilities tests
* @author <a href="mailto:jkalina@redhat.com">Jan Kalina</a>
*/
public class DigestUtilTest {
MessageDigest md;
@Before
public void init() throws Exception {
md = MessageDigest.getInstance(messageDigestAlgorithm(SaslMechanismInformation.Names.DIGEST_MD5));
}
@Test
public void testH_A1() throws Exception {
assertEquals("a2549853149b0536f01f0b850c643c57", ByteIterator.ofBytes(
H_A1(md, userRealmPasswordDigest(md, "chris", "elwood.innosoft.com", "secret".toCharArray()),
"OA6MG9tEQGm2hh".getBytes(StandardCharsets.UTF_8), "OA6MHXh6VqTrRk".getBytes(StandardCharsets.UTF_8), null, StandardCharsets.UTF_8)).hexEncode().drainToString());
assertEquals("7f94ea5b1eb1b0573cca321e2b517b63", ByteIterator.ofBytes(
H_A1(md, userRealmPasswordDigest(md, "chris", "elwood.innosoft.com", "secret".toCharArray()),
"OA9BSXrbuRhWay".getBytes(StandardCharsets.UTF_8), "OA9BSuZWMSpW8m".getBytes(StandardCharsets.UTF_8), "chris", StandardCharsets.UTF_8)).hexEncode().drainToString());
assertEquals("4e863a809aa7f7cc191be93705967394", ByteIterator.ofBytes(
H_A1(md, userRealmPasswordDigest(md, "\u0438\u4F60\uD83C\uDCA1", "realm.\u0438\u4F60\uD83C\uDCA1.com", "\u0438\u4F60\uD83C\uDCA1".toCharArray()),
"sn\u0438\u4F60\uD83C\uDCA1".getBytes(StandardCharsets.UTF_8), "cn\u0438\u4F60\uD83C\uDCA1".getBytes(StandardCharsets.UTF_8), null,
StandardCharsets.UTF_8)).hexEncode().drainToString());
}
@Test
public void testDigestResponse() throws Exception {
assertEquals("d388dad90d4bbd760a152321f2143af7", new String(
digestResponse(md, CodePointIterator.ofString("a2549853149b0536f01f0b850c643c57").hexDecode().drain(),
"OA6MG9tEQGm2hh".getBytes(StandardCharsets.UTF_8), 1, "OA6MHXh6VqTrRk".getBytes(StandardCharsets.UTF_8), null, "auth", "imap/elwood.innosoft.com", true), StandardCharsets.UTF_8));
assertEquals("ea40f60335c427b5527b84dbabcdfffd", new String(
digestResponse(md, CodePointIterator.ofString("a2549853149b0536f01f0b850c643c57").hexDecode().drain(),
"OA6MG9tEQGm2hh".getBytes(StandardCharsets.UTF_8), 1, "OA6MHXh6VqTrRk".getBytes(StandardCharsets.UTF_8), null, "auth", "imap/elwood.innosoft.com", false), StandardCharsets.UTF_8));
assertEquals("aa4e81f1c6656350f7bce05d436665de", new String(
digestResponse(md, CodePointIterator.ofString("7f94ea5b1eb1b0573cca321e2b517b63").hexDecode().drain(),
"OA9BSXrbuRhWay".getBytes(StandardCharsets.UTF_8), 1, "OA9BSuZWMSpW8m".getBytes(StandardCharsets.UTF_8), "chris", "auth", "acap/elwood.innosoft.com", true), StandardCharsets.UTF_8));
assertEquals("af3ca83a805d4cfa00675a17315475c4", new String(
digestResponse(md, CodePointIterator.ofString("7f94ea5b1eb1b0573cca321e2b517b63").hexDecode().drain(),
"OA9BSXrbuRhWay".getBytes(StandardCharsets.UTF_8), 1, "OA9BSuZWMSpW8m".getBytes(StandardCharsets.UTF_8), "chris", "auth", "acap/elwood.innosoft.com", false), StandardCharsets.UTF_8));
}
@Test
/* LHEX = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" */
public void testConvertToHexBytesWithLeftPadding() throws Exception {
assertEquals("00000001", new String(convertToHexBytesWithLeftPadding(1, 8), StandardCharsets.UTF_8));
assertEquals("0000002", new String(convertToHexBytesWithLeftPadding(2, 7), StandardCharsets.UTF_8));
assertEquals("000a", new String(convertToHexBytesWithLeftPadding(10, 4), StandardCharsets.UTF_8));
assertEquals("abc", new String(convertToHexBytesWithLeftPadding(0xABC, 3), StandardCharsets.UTF_8));
}
@Test
public void testCreate3desSubKey() throws Exception {
byte[] input1 = CodePointIterator.ofString("FFFFFFFFFFFFFF").hexDecode().drain();
byte[] output1 = create3desSubKey(input1, 0);
assertEquals("FEFEFEFEFEFEFEFE".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output1).hexEncode().drainToString());
byte[] input2 = CodePointIterator.ofString("d7c920cf2564cec39c570490f7ea").hexDecode().drain();
byte[] output2 = create3desSubKey(input2, 0);
assertEquals("D6E54919F22A929D".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output2).hexEncode().drainToString());
byte[] input3 = CodePointIterator.ofString("d7c920cf2564cec39c570490f7ea").hexDecode().drain();
byte[] output3 = create3desSubKey(input3, 7);
assertEquals("C2CE15E04986DFD5".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output3).hexEncode().drainToString());
}
@Test
public void testComputeHmac() throws Exception {
Mac mac = Mac.getInstance("HmacMD5");
byte[] message = CodePointIterator.ofString("11223344").hexDecode().drain();
byte[] kc = CodePointIterator.ofString("9fdbff3d48c87e74bd89460e2462c73a").hexDecode().drain();
byte[] output = computeHMAC(kc, 0, mac, message, 0, message.length);
assertEquals("EF3E40D7B5A64C1DAE6B".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output).hexEncode().drainToString());
}
@Test
public void testIntegerByteOrdered() throws Exception {
byte[] output0 = new byte[4];
integerByteOrdered(0x0, output0, 0, 4);
assertEquals("00000000".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output0).hexEncode().drainToString());
byte[] output1 = new byte[4];
integerByteOrdered(0x1, output1, 0, 4);
assertEquals("00000001".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output1).hexEncode().drainToString());
byte[] output1234 = new byte[6];
integerByteOrdered(0x1234, output1234, 1, 4);
assertEquals("000000123400".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(output1234).hexEncode().drainToString());
byte[] outputFFFFFFFF = new byte[4];
integerByteOrdered(0xFFFFFFFF, outputFFFFFFFF, 0, 4);
assertEquals("FFFFFFFF".toLowerCase(Locale.ROOT), ByteIterator.ofBytes(outputFFFFFFFF).hexEncode().drainToString());
}
@Test
public void testDecodeByteOrderedInteger() throws Exception {
byte[] input0 = CodePointIterator.ofString("00000000").hexDecode().drain();
assertEquals(0x0, decodeByteOrderedInteger(input0, 0, 4));
byte[] input1 = CodePointIterator.ofString("00000001").hexDecode().drain();
assertEquals(0x1, decodeByteOrderedInteger(input1, 0, 4));
byte[] input1234 = CodePointIterator.ofString("000000123400").hexDecode().drain();
assertEquals(0x1234, decodeByteOrderedInteger(input1234, 1, 4));
byte[] input1234b = CodePointIterator.ofString("000000123400").hexDecode().drain();
assertEquals(0x1234, decodeByteOrderedInteger(input1234b, 3, 2));
byte[] inputFFFFFFFF = CodePointIterator.ofString("FFFFFFFF").hexDecode().drain();
assertEquals(0xFFFFFFFF, decodeByteOrderedInteger(inputFFFFFFFF, 0, 4));
byte[] inputFF = CodePointIterator.ofString("000000FF").hexDecode().drain();
assertEquals(0xFF, decodeByteOrderedInteger(inputFF, 0, 4));
}
}