Skip to content
This repository has been archived by the owner on Feb 16, 2022. It is now read-only.

Commit

Permalink
Continuing on DefaultSecretService
Browse files Browse the repository at this point in the history
  • Loading branch information
sawano committed Mar 22, 2017
1 parent 11e6df3 commit bc9682a
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 13 deletions.
7 changes: 7 additions & 0 deletions src/main/java/se/sawano/java/security/otp/SharedSecret.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ public static SharedSecret fromBase32(final String base32String, final ShaAlgori
notNull(algorithm);

final byte[] bytes = new Base32(false).decode(base32String);
return from(bytes, algorithm);
}

public static SharedSecret from(final byte[] bytes, final ShaAlgorithm algorithm) {
notNull(bytes);
notNull(algorithm);

return new SharedSecret(bytes, algorithm);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,40 @@

package se.sawano.java.security.otp.impl;

import org.apache.commons.codec.binary.Base64;
import se.sawano.java.security.otp.SecretService;
import se.sawano.java.security.otp.ShaAlgorithm;
import se.sawano.java.security.otp.SharedSecret;

import java.security.SecureRandom;
import java.util.Random;
import java.util.HashMap;
import java.util.Map;

import static org.apache.commons.lang3.Validate.notNull;

public class DefaultSecretService implements SecretService {

private final Random random;
private final RandomSupplier random;
private final Map<ShaAlgorithm, Integer> algorithmToNumberOfBytes = new HashMap<>();

public DefaultSecretService() {
this(new SecureRandom());
this(new DefaultRandomSupplier());
}

public DefaultSecretService(final Random random) {
public DefaultSecretService(final RandomSupplier random) {
notNull(random);

this.random = random;
// TODO consolidate with SharedSecret?
algorithmToNumberOfBytes.put(ShaAlgorithm.SHA1, 20);
algorithmToNumberOfBytes.put(ShaAlgorithm.SHA256, 32);
algorithmToNumberOfBytes.put(ShaAlgorithm.SHA512, 64);
}

@Override
public SharedSecret generateSharedSecret() {
// TODO implement
final byte[] bytes = new byte[10];
public SharedSecret generateSharedSecret(final ShaAlgorithm algorithm) {
final byte[] bytes = new byte[algorithmToNumberOfBytes.get(algorithm)];

random.nextBytes(bytes);
final String s1 = java.util.Base64.getEncoder().encodeToString(bytes);
final String s = Base64.encodeBase64String(bytes);
return null;

return SharedSecret.from(bytes, algorithm);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ private static byte[] toHexBytes(final long value) {
}
}

/**
* Truncates the hash. I.e. converts it to binary code, which is a 31-bit, unsigned,
* big-endian integer; the first byte is masked with a 0x7f. (see RFC4226)
*/
private static int truncate(final byte[] hash) {
final int offset = hash[hash.length - 1] & 0xf;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2017 Daniel Sawano
*
* 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 se.sawano.java.security.otp.impl;

import org.junit.Test;
import se.sawano.java.security.otp.ShaAlgorithm;
import se.sawano.java.security.otp.SharedSecret;
import se.sawano.java.security.otp.TOTP;

import static org.junit.Assert.assertEquals;
import static se.sawano.java.security.otp.ShaAlgorithm.*;

public class DefaultSecretServiceTest {

public static final String ASCII_SECRET_FROM_RFC6238 = "12345678901234567890";
public static final String EXPECTED_SHA1_HEX_SECRET_FROM_RFC_6238_EXAMPLE = "3132333435363738393031323334353637383930";
public static final String EXPECTED_SHA256_HEX_SECRET_FROM_RFC_6238_EXAMPLE = "3132333435363738393031323334353637383930313233343536373839303132";
public static final String EXPECTED_SHA512_HEX_SECRET_FROM_RFC_6238_EXAMPLE = "3132333435363738393031323334353637383930" +
"3132333435363738393031323334353637383930" +
"3132333435363738393031323334353637383930" +
"31323334";
private ShaAlgorithm algorithm;
private SharedSecret secret;

@Test
public void should_generate_sha1_secret() throws Exception {
givenAlgorithm(SHA1);

whenGeneratingSecret();

thenSecretShouldHaveCorrectAlgorithm();
thenNumberOfBytesInSecretIs(20);
thenHexSecretIs(EXPECTED_SHA1_HEX_SECRET_FROM_RFC_6238_EXAMPLE);
}

@Test
public void should_generate_sha256_secret() throws Exception {
givenAlgorithm(SHA256);

whenGeneratingSecret();

thenSecretShouldHaveCorrectAlgorithm();
thenNumberOfBytesInSecretIs(32);
thenHexSecretIs(EXPECTED_SHA256_HEX_SECRET_FROM_RFC_6238_EXAMPLE);
}

@Test
public void should_generate_sha512_secret() throws Exception {
givenAlgorithm(SHA512);

whenGeneratingSecret();

thenSecretShouldHaveCorrectAlgorithm();
thenNumberOfBytesInSecretIs(64);
thenHexSecretIs(EXPECTED_SHA512_HEX_SECRET_FROM_RFC_6238_EXAMPLE);
}

@Test
public void should_print_totp() throws Exception {
final String secretB32 = "ZEJHUB2WISYTMOUMDNM7GO5URLKS7TXC";

final SharedSecret secret = SharedSecret.fromBase32(secretB32, SHA1);

final TOTP totp = new DefaultTOTPService().create(secret, TOTP.Length.SIX, secret.algorithm());

System.out.println(totp.value());
}

private void givenAlgorithm(final ShaAlgorithm algorithm) {
this.algorithm = algorithm;
}

private void whenGeneratingSecret() {
final String s = (ASCII_SECRET_FROM_RFC6238 + ASCII_SECRET_FROM_RFC6238 + ASCII_SECRET_FROM_RFC6238 + ASCII_SECRET_FROM_RFC6238);
final RandomSupplier randomSupplier = bytes -> System.arraycopy(s.getBytes(), 0, bytes, 0, bytes.length);
final DefaultSecretService secretService = new DefaultSecretService(randomSupplier);
secret = secretService.generateSharedSecret(algorithm);
}

private void thenSecretShouldHaveCorrectAlgorithm() {
assertEquals(algorithm, secret.algorithm());
}

private void thenNumberOfBytesInSecretIs(final int expectedNumberOfBytes) {
assertEquals(expectedNumberOfBytes, secret.value().length);
}

private void thenHexSecretIs(final String expectedHexSecret) {
assertEquals(expectedHexSecret, secret.asHexString());
}
}

0 comments on commit bc9682a

Please sign in to comment.