Skip to content

Commit

Permalink
Merge pull request #76 from filip26/issue/75
Browse files Browse the repository at this point in the history
Fix Did.from and Did.isDid methods
  • Loading branch information
filip26 authored Jul 10, 2024
2 parents 5b296a4 + 0f09b5b commit 4afe1c1
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 93 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ An implementation of the [Decentralized Identifiers (DIDs) v1.0](https://www.w3.
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>carbon-did</artifactId>
<version>0.4.0</version>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>com.apicatalog</groupId>
Expand All @@ -39,7 +39,7 @@ Android 12+ (API Level >=32)


```gradle
implementation("com.apicatalog:carbon-did:0.4.0")
implementation("com.apicatalog:carbon-did:0.5.0")
implementation("com.apicatalog:copper-multibase:0.5.0")
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>com.apicatalog</groupId>
<artifactId>carbon-did</artifactId>

<version>0.4.0</version>
<version>0.5.0</version>
<packaging>jar</packaging>

<url>https://github.com/filip26/carbon-decentralized-identifiers</url>
Expand Down
145 changes: 73 additions & 72 deletions src/main/java/com/apicatalog/did/Did.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.function.IntPredicate;

public class Did implements Serializable {

private static final long serialVersionUID = -3127318269811273712L;

public static final String SCHEME = "did";
/*
* method-char = %x61-7A / DIGIT
*/
static final IntPredicate METHOD_CHAR = ch -> (0x61 <= ch && ch <= 0x7A) || ('0' <= ch && ch <= '9');

public static final String SCHEME = "did";

protected final String method;
protected final String version;
protected final String methodSpecificId;
protected final String specificId;

protected Did(final String method, final String version, final String methodSpecificId) {
protected Did(final String method, final String specificId) {
this.method = method;
this.version = version;
this.methodSpecificId = methodSpecificId;
this.specificId = specificId;
}

public static boolean isDid(final URI uri) {
Expand All @@ -29,29 +33,32 @@ public static boolean isDid(final URI uri) {
|| isNotBlank(uri.getHost())
|| isNotBlank(uri.getPath())
|| isNotBlank(uri.getQuery())
|| isNotBlank(uri.getFragment())
) {
return false;
}
|| isNotBlank(uri.getFragment())) {
return false;
}

final String[] parts = uri.getSchemeSpecificPart().split(":");
final String[] parts = uri.getSchemeSpecificPart().split(":", 2);

return parts.length == 2 || parts.length == 3;
return parts.length == 2
&& parts[0].length() > 0
&& parts[1].length() > 0
&& parts[0].codePoints().allMatch(METHOD_CHAR)
;
}

public static boolean isDid(final String uri) {

if (isBlank(uri)) {
if (uri == null) {
return false;
}

final String[] parts = uri.split(":");
final String[] parts = uri.split(":", 3);

return (parts.length == 3 || parts.length == 4)
return parts.length == 3
&& Did.SCHEME.equalsIgnoreCase(parts[0])
&& !parts[parts.length - 1].contains("/") // path
&& !parts[parts.length - 1].contains("?") // query
&& !parts[parts.length - 1].contains("#") // fragment
&& parts[1].length() > 0
&& parts[2].length() > 0
&& parts[1].codePoints().allMatch(METHOD_CHAR)
;
}

Expand All @@ -61,19 +68,21 @@ public static boolean isDid(final String uri) {
* @param uri The source URI to be transformed into DID
* @return The new DID
*
* @throws NullPointerException
* If {@code uri} is {@code null}
* @throws NullPointerException If {@code uri} is {@code null}
*
* @throws IllegalArgumentException
* If the given {@code uri} is not valid DID
* @throws IllegalArgumentException If the given {@code uri} is not valid DID
*/
public static Did from(final URI uri) {

if (!isDid(uri)) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID key, does not start with 'did:'.");
if (uri == null) {
throw new IllegalArgumentException("The DID must not be null.");
}

return from(uri, uri.getSchemeSpecificPart().split(":"), 3);
if (!Did.SCHEME.equalsIgnoreCase(uri.getScheme())) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must start with 'did:' prefix.");
}

return from(uri, uri.getSchemeSpecificPart().split(":", 2));
}

/**
Expand All @@ -82,59 +91,58 @@ public static Did from(final URI uri) {
* @param uri The source URI to be transformed into DID
* @return The new DID
*
* @throws NullPointerException
* If {@code uri} is {@code null}
* @throws NullPointerException If {@code uri} is {@code null}
*
* @throws IllegalArgumentException
* If the given {@code uri} is not valid DID
* @throws IllegalArgumentException If the given {@code uri} is not valid DID
*/
public static Did from(final String uri) {
if (uri == null || uri.length() == 0) {
throw new IllegalArgumentException("The DID must not be null or blank string.");
}

if (!isDid(uri)) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID key, does not start with 'did:'.");
final String[] parts = uri.split(":", 3);

if (parts.length != 3) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must be in form 'did:method:method-specific-id'.");
}

return from(uri, uri.split(":"), 4);
}
if (!Did.SCHEME.equalsIgnoreCase(parts[0])) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must start with 'did:' prefix.");
}

protected static Did from(final Object uri, final String[] parts, int max) {
return from(uri, new String[] { parts[1], parts[2] });
}

if (parts.length < max - 1
|| parts.length > max
|| isBlank(parts[max - 3])
|| isBlank(parts[max - 2])
) {
protected static Did from(final Object uri, final String[] parts) {
if (parts.length != 2) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must be in form 'did:method:method-specific-id'.");
}

String methodSpecificId = parts[max - 2];
String version = "1"; // default DID version

if (parts.length == max) {
if (isBlank(parts[max - 1])) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must be in form 'did:method:method-specific-id'.");
}
version = parts[max - 2];
methodSpecificId = parts[max - 1];
// check method
if (parts[0].length() == 0
|| !parts[0].codePoints().allMatch(METHOD_CHAR)) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, method [" + parts[0] + "] syntax is blank or invalid.");
}

// check method specific id
if (parts[1].length() == 0) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, method specific id [" + parts[1] + "] is blank.");
}

return new Did(parts[max - 3], version, methodSpecificId);
return new Did(parts[0], parts[1]);
}

public String getMethod() {
return method;
}

public String getVersion() {
return version;
}

public String getMethodSpecificId() {
return methodSpecificId;
return specificId;
}

public URI toUri() {
try {
return new URI(SCHEME, method + ":" + methodSpecificId, null);
return new URI(SCHEME, method + ":" + specificId, null);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
Expand All @@ -150,23 +158,17 @@ public DidUrl asDidUrl() {

@Override
public String toString() {
final StringBuilder builder = new StringBuilder()
.append(SCHEME)
.append(':')
.append(method)
.append(':');

if (!"1".equals(version)) {
builder
.append(version)
.append(':');
}
return builder.append(methodSpecificId).toString();
return new StringBuilder()
.append(SCHEME)
.append(':')
.append(method)
.append(':')
.append(specificId).toString();
}

@Override
public int hashCode() {
return Objects.hash(method, methodSpecificId, version);
return Objects.hash(method, specificId);
}

@Override
Expand All @@ -181,16 +183,15 @@ public boolean equals(final Object obj) {
return false;
}
Did other = (Did) obj;
return Objects.equals(method, other.method) && Objects.equals(methodSpecificId, other.methodSpecificId)
&& Objects.equals(version, other.version);
return Objects.equals(method, other.method) && Objects.equals(specificId, other.specificId);

}

static final boolean isNotBlank(String value) {
return value != null && !value.trim().isEmpty();
}

static final boolean isBlank(String value) {
return value == null || value.trim().isEmpty();
}

}
8 changes: 4 additions & 4 deletions src/main/java/com/apicatalog/did/DidUrl.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
public class DidUrl extends Did {

private static final long serialVersionUID = 5752880077497569763L;

protected final String path;
protected final String query;
protected final String fragment;

protected DidUrl(Did did, String path, String query, String fragment) {
super(did.method, did.version, did.methodSpecificId);
super(did.method, did.specificId);
this.path = path;
this.query = query;
this.fragment = fragment;
Expand All @@ -31,7 +31,7 @@ public static DidUrl from(final URI uri) {
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID URL, does not start with 'did:'.");
}

Did did = from(uri, uri.getSchemeSpecificPart().split(":"), 3);
Did did = from(uri, uri.getSchemeSpecificPart().split(":", 2));

return new DidUrl(did, uri.getPath(), uri.getQuery(), uri.getFragment());
}
Expand All @@ -56,7 +56,7 @@ public static boolean isDidUrl(final String uri) {
@Override
public URI toUri() {
try {
return new URI(SCHEME, method + ":" + methodSpecificId, path, query, fragment);
return new URI(SCHEME, method + ":" + specificId, path, query, fragment);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
Expand Down
40 changes: 29 additions & 11 deletions src/main/java/com/apicatalog/did/key/DidKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
/**
* Immutable DID Key
* <p>
* did-key-format := did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type,
* raw-public-key-bytes))
* did-key-format := did:key:[version]:MULTIBASE(multiencodedKey)
* </p>
*
* @see <a href=
Expand All @@ -20,18 +19,23 @@
*/
public class DidKey extends Did {

private static final long serialVersionUID = 3710900614215756688L;
private static final long serialVersionUID = 1343361455801198884L;

public static final String METHOD_KEY = "key";

public static final String DEFAULT_VERSION = "1";

protected final String version;

protected final Multibase base;

protected final byte[] encodedKey;
protected final byte[] debased;

protected DidKey(String version, String encoded, Multibase base, byte[] debased) {
super(METHOD_KEY, version, encoded);
protected DidKey(String version, String specificId, Multibase base, byte[] debased) {
super(METHOD_KEY, specificId);
this.base = base;
this.encodedKey = debased;
this.version = version;
this.debased = debased;
}

/**
Expand Down Expand Up @@ -63,11 +67,21 @@ public static final DidKey from(final Did did, final MultibaseDecoder bases) {
throw new IllegalArgumentException("The given DID method [" + did.getMethod() + "] is not 'key'. DID [" + did.toString() + "].");
}

final Multibase base = bases.getBase(did.getMethodSpecificId()).orElseThrow(() -> new IllegalArgumentException("Unsupported did:key base encoding. DID [" + did.toString() + "]."));
final String[] parts = did.getMethodSpecificId().split(":", 2);

String version = DEFAULT_VERSION;
String encoded = parts[0];

if (parts.length == 2) {
version = parts[0];
encoded = parts[1];
}

final Multibase base = bases.getBase(encoded).orElseThrow(() -> new IllegalArgumentException("Unsupported did:key base encoding. DID [" + did.toString() + "]."));

final byte[] debased = base.decode(did.getMethodSpecificId());
final byte[] debased = base.decode(encoded);

return new DidKey(did.getVersion(), did.getMethodSpecificId(), base, debased);
return new DidKey(version, encoded, base, debased);
}

public static final DidKey create(Multibase base, byte[] key) {
Expand All @@ -93,6 +107,10 @@ public Multibase getBase() {
}

public byte[] getKey() {
return encodedKey;
return debased;
}

public String getVersion() {
return version;
}
}
Loading

0 comments on commit 4afe1c1

Please sign in to comment.