Skip to content

Commit

Permalink
Version 2.0 Implemented padding (#4) and ability to change URLs (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
GideonLeGrange committed Jul 14, 2020
1 parent 3c38962 commit 6c92f5f
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 29 deletions.
32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ This API provides an easy way of accessing the account and password verification

[![Build Status](https://travis-ci.org/GideonLeGrange/haveibeenpwned.svg?branch=master)](https://travis-ci.org/GideonLeGrange/haveibeenpwned)

**The current version is 1.1**

There's not much difference between 1.0 and 1.1, apart from:
* Dependencies have been updated to address security concerns reported in `retrofit` prior to version 2.5
* In version 1.1 you can sub-class the `HaveIBeenPwndApi` class if you wish

**The current version is 2.0**

Version 2.0 brings the following changes:
* The API supports padding for pwnd passwords (see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/)
* A builder pattern is added in place of the constructor to allow for configuration of the following:
* The User-Agent (was configured as part of the constructor)
* If padding (as per above) should be added
* The URL to use for pwnd passwords (advanced feature, don't use)
* The URL to use for breach data (advanced feature, don't use)
* The builder pattern must be used

# Getting the API

Maven users can use the artifact from Maven Central with this dependency:
Expand All @@ -21,7 +26,7 @@ Maven users can use the artifact from Maven Central with this dependency:
<dependency>
<groupId>me.legrange</groupId>
<artifactId>haveibeenpwned</artifactId>
<version>1.1</version>
<version>2.0</version>
</dependency>
```

Expand All @@ -33,15 +38,22 @@ The purpose of the API see if an account (email address) or password has been li
To use the API you need to instantiate an instance of it, and then call one of the methods. I can't be simpler:

```java
HaveIBeenPwndApi hibp = new HaveIBeenPwndApi();
HaveIBeenPwndApi hibp = HaveIBeenPwndBuilder().create().build();
```

A slightly better way is to use the constructor that allows you to set the user-agent sent to the remote API to identify your application:
But if you wish to configure the API, `HaveIBeenPwnedBuilder` allows you to set if you wish to add padding to pwnd password checks [see this blog for details](https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/),
what user-agent to use, and it even allows you to change the pwnd password and breach URLs to use (for testing, don't do this).

For example, here we enable padding and set the user-agent:

```java
HaveIBeenPwndApi hibp = new HaveIBeenPwndApi("My-Pwnage-Testing-App");
HaveIBeenPwndApi hibp = HaveIBeenPwndBuilder.create()
.addPadding(true)
.withUserAgent("Pwn-Checker-1.0")
.build();
```


In the following examples we assume the API has been instantiated and is called ```hibp```.

## See if a specific account or password has been breached
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>me.legrange</groupId>
<artifactId>haveibeenpwned</artifactId>
<version>1.1</version>
<version>2.0</version>
<packaging>jar</packaging>
<url>https://github.com/GideonLeGrange/haveibeenpwned</url>
<licenses>
Expand Down
26 changes: 9 additions & 17 deletions src/main/java/me/legrange/haveibeenpwned/HaveIBeenPwndApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,12 @@ public class HaveIBeenPwndApi {

private final HaveIBeenPwndService hibpService;
private final PwnedPasswordsService ppwService;
private static final String HIBP_REST_URL = "https://haveibeenpwned.com/api/v2/";
private static final String PPW_REST_URL = "https://api.pwnedpasswords.com/";
private static final String DEFAULT_USER_AGENT = "HaveIBeenPwndJava-v1";

/**
* Create a new instance of the API with the default user agent
*/
public HaveIBeenPwndApi() {
this(DEFAULT_USER_AGENT);
}
private final boolean addPadding;

/**
* Create a new instance of the API with the given user agent.
*
* @param userAgent The useragent to use.
*/
public HaveIBeenPwndApi(String userAgent) {
HaveIBeenPwndApi(String hibpUrl, String ppwUrl, boolean addPadding, String userAgent) {
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(chain -> {
Request request = chain.request().newBuilder().addHeader("User-Agent", userAgent).build();
return chain.proceed(request);
Expand All @@ -56,18 +45,19 @@ public HaveIBeenPwndApi(String userAgent) {
.create();

Retrofit retrofit = new Retrofit.Builder()
.baseUrl(HIBP_REST_URL)
.baseUrl(hibpUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build();

hibpService = retrofit.create(HaveIBeenPwndService.class);
retrofit = new Retrofit.Builder()
.baseUrl(PPW_REST_URL)
.baseUrl(ppwUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.client(client)
.build();
ppwService = retrofit.create(PwnedPasswordsService.class);
this.addPadding = addPadding;
}

/**
Expand Down Expand Up @@ -153,7 +143,7 @@ public List<Paste> getAllPastesForAccount(String account) throws HaveIBeenPwndEx
* @throws HaveIBeenPwndException Thrown if an error occurs
*/
public List<PwnedHash> searchByRange(String hash5) throws HaveIBeenPwndException {
String res = callService(ppwService.searchByRange(hash5)).orElse("");
String res = callService(ppwService.searchByRange(hash5, addPadding)).orElse("");
if (!res.isEmpty()) {
Stream<String> lines = Arrays.asList(res.split("\n")).stream();
return lines.map(line -> line.replace("\r", "").split(":"))
Expand Down Expand Up @@ -198,7 +188,9 @@ public boolean isPlainPasswordPwned(String password) throws HaveIBeenPwndExcepti
*/
public boolean isHashPasswordPwned(String pwHash) throws HaveIBeenPwndException {
String hash5 = pwHash.substring(0, 5);
List<PwnedHash> hashes = searchByRange(hash5);
List<PwnedHash> hashes = searchByRange(hash5).stream()
.filter(hash -> hash.getCount() > 0)
.collect(Collectors.toList());
return hashes.stream().anyMatch(hash -> (hash5 + hash.getHash()).equals(pwHash));
}

Expand Down
78 changes: 78 additions & 0 deletions src/main/java/me/legrange/haveibeenpwned/HaveIBeenPwndBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package me.legrange.haveibeenpwned;

/** A builder pattern to setup the HaveIBeenPwndApi the way you want.
*
* @author GideonLeGrange
*/
public final class HaveIBeenPwndBuilder {

private static final String HIBP_REST_URL = "https://haveibeenpwned.com/api/v2/";
private static final String PPW_REST_URL = "https://api.pwnedpasswords.com/";
private static final String DEFAULT_USER_AGENT = "HaveIBeenPwndJava-v1";

private boolean addPadding = false;
private String haveIbeenPwndUrl = HIBP_REST_URL;
private String pwndPasswordsUrl = PPW_REST_URL;
private String userAgent = DEFAULT_USER_AGENT;

/** Create a new builder.
*
* @return The builder
*/
public static HaveIBeenPwndBuilder create() {
return new HaveIBeenPwndBuilder();
}

/** Change the URL for the HaveIBeenPwnd breach service.
*
* @param url The URL to use
* @return The builder
*/
public HaveIBeenPwndBuilder withHaveIBeenPwndUrl(String url) {
this.haveIbeenPwndUrl = url;
return this;
}

/** Change the URL for the HaveIBeenPwnd password service.
*
* @param url The URL to use
* @return The builder
*/
public HaveIBeenPwndBuilder withPwndPasswordsUrl(String url) {
this.pwndPasswordsUrl = url;
return this;
}

/** Change the User-Agent to send with an HTTP request from the default
*
* @param userAgent The URL to use
* @return The builder
*/
public HaveIBeenPwndBuilder withUserAgent(String userAgent) {
this.userAgent = userAgent;
return this;
}

/** Set if padding on pwnd password calls is enabled.
*
* @param addPadding Is padding is required?
* @return The builder
*/
public HaveIBeenPwndBuilder addPadding(boolean addPadding) {
this.addPadding = addPadding;
return this;
}

/** Build the API
*
* @return The API
*/
public HaveIBeenPwndApi build() {
return new HaveIBeenPwndApi(haveIbeenPwndUrl, pwndPasswordsUrl, addPadding, userAgent);
}

private HaveIBeenPwndBuilder() {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Path;

/**
Expand All @@ -11,6 +12,6 @@
interface PwnedPasswordsService {

@GET("range/{hash5}")
Call<String> searchByRange(@Path("hash5") String hash5);
Call<String> searchByRange(@Path("hash5") String hash5, @Header("Add-Padding") boolean addPadding);

}

0 comments on commit 6c92f5f

Please sign in to comment.