Skip to content

Commit 4afe1c1

Browse files
authored
Merge pull request #76 from filip26/issue/75
Fix Did.from and Did.isDid methods
2 parents 5b296a4 + 0f09b5b commit 4afe1c1

File tree

8 files changed

+207
-93
lines changed

8 files changed

+207
-93
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ An implementation of the [Decentralized Identifiers (DIDs) v1.0](https://www.w3.
2424
<dependency>
2525
<groupId>com.apicatalog</groupId>
2626
<artifactId>carbon-did</artifactId>
27-
<version>0.4.0</version>
27+
<version>0.5.0</version>
2828
</dependency>
2929
<dependency>
3030
<groupId>com.apicatalog</groupId>
@@ -39,7 +39,7 @@ Android 12+ (API Level >=32)
3939

4040

4141
```gradle
42-
implementation("com.apicatalog:carbon-did:0.4.0")
42+
implementation("com.apicatalog:carbon-did:0.5.0")
4343
implementation("com.apicatalog:copper-multibase:0.5.0")
4444
```
4545

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<groupId>com.apicatalog</groupId>
88
<artifactId>carbon-did</artifactId>
99

10-
<version>0.4.0</version>
10+
<version>0.5.0</version>
1111
<packaging>jar</packaging>
1212

1313
<url>https://github.com/filip26/carbon-decentralized-identifiers</url>

src/main/java/com/apicatalog/did/Did.java

+73-72
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@
44
import java.net.URI;
55
import java.net.URISyntaxException;
66
import java.util.Objects;
7+
import java.util.function.IntPredicate;
78

89
public class Did implements Serializable {
910

1011
private static final long serialVersionUID = -3127318269811273712L;
1112

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

18+
public static final String SCHEME = "did";
19+
1420
protected final String method;
15-
protected final String version;
16-
protected final String methodSpecificId;
21+
protected final String specificId;
1722

18-
protected Did(final String method, final String version, final String methodSpecificId) {
23+
protected Did(final String method, final String specificId) {
1924
this.method = method;
20-
this.version = version;
21-
this.methodSpecificId = methodSpecificId;
25+
this.specificId = specificId;
2226
}
2327

2428
public static boolean isDid(final URI uri) {
@@ -29,29 +33,32 @@ public static boolean isDid(final URI uri) {
2933
|| isNotBlank(uri.getHost())
3034
|| isNotBlank(uri.getPath())
3135
|| isNotBlank(uri.getQuery())
32-
|| isNotBlank(uri.getFragment())
33-
) {
34-
return false;
35-
}
36+
|| isNotBlank(uri.getFragment())) {
37+
return false;
38+
}
3639

37-
final String[] parts = uri.getSchemeSpecificPart().split(":");
40+
final String[] parts = uri.getSchemeSpecificPart().split(":", 2);
3841

39-
return parts.length == 2 || parts.length == 3;
42+
return parts.length == 2
43+
&& parts[0].length() > 0
44+
&& parts[1].length() > 0
45+
&& parts[0].codePoints().allMatch(METHOD_CHAR)
46+
;
4047
}
4148

4249
public static boolean isDid(final String uri) {
4350

44-
if (isBlank(uri)) {
51+
if (uri == null) {
4552
return false;
4653
}
4754

48-
final String[] parts = uri.split(":");
55+
final String[] parts = uri.split(":", 3);
4956

50-
return (parts.length == 3 || parts.length == 4)
57+
return parts.length == 3
5158
&& Did.SCHEME.equalsIgnoreCase(parts[0])
52-
&& !parts[parts.length - 1].contains("/") // path
53-
&& !parts[parts.length - 1].contains("?") // query
54-
&& !parts[parts.length - 1].contains("#") // fragment
59+
&& parts[1].length() > 0
60+
&& parts[2].length() > 0
61+
&& parts[1].codePoints().allMatch(METHOD_CHAR)
5562
;
5663
}
5764

@@ -61,19 +68,21 @@ public static boolean isDid(final String uri) {
6168
* @param uri The source URI to be transformed into DID
6269
* @return The new DID
6370
*
64-
* @throws NullPointerException
65-
* If {@code uri} is {@code null}
71+
* @throws NullPointerException If {@code uri} is {@code null}
6672
*
67-
* @throws IllegalArgumentException
68-
* If the given {@code uri} is not valid DID
73+
* @throws IllegalArgumentException If the given {@code uri} is not valid DID
6974
*/
7075
public static Did from(final URI uri) {
7176

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

76-
return from(uri, uri.getSchemeSpecificPart().split(":"), 3);
81+
if (!Did.SCHEME.equalsIgnoreCase(uri.getScheme())) {
82+
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must start with 'did:' prefix.");
83+
}
84+
85+
return from(uri, uri.getSchemeSpecificPart().split(":", 2));
7786
}
7887

7988
/**
@@ -82,59 +91,58 @@ public static Did from(final URI uri) {
8291
* @param uri The source URI to be transformed into DID
8392
* @return The new DID
8493
*
85-
* @throws NullPointerException
86-
* If {@code uri} is {@code null}
94+
* @throws NullPointerException If {@code uri} is {@code null}
8795
*
88-
* @throws IllegalArgumentException
89-
* If the given {@code uri} is not valid DID
96+
* @throws IllegalArgumentException If the given {@code uri} is not valid DID
9097
*/
9198
public static Did from(final String uri) {
99+
if (uri == null || uri.length() == 0) {
100+
throw new IllegalArgumentException("The DID must not be null or blank string.");
101+
}
92102

93-
if (!isDid(uri)) {
94-
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID key, does not start with 'did:'.");
103+
final String[] parts = uri.split(":", 3);
104+
105+
if (parts.length != 3) {
106+
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must be in form 'did:method:method-specific-id'.");
95107
}
96108

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

100-
protected static Did from(final Object uri, final String[] parts, int max) {
113+
return from(uri, new String[] { parts[1], parts[2] });
114+
}
101115

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

110-
String methodSpecificId = parts[max - 2];
111-
String version = "1"; // default DID version
112-
113-
if (parts.length == max) {
114-
if (isBlank(parts[max - 1])) {
115-
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, must be in form 'did:method:method-specific-id'.");
116-
}
117-
version = parts[max - 2];
118-
methodSpecificId = parts[max - 1];
121+
// check method
122+
if (parts[0].length() == 0
123+
|| !parts[0].codePoints().allMatch(METHOD_CHAR)) {
124+
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, method [" + parts[0] + "] syntax is blank or invalid.");
125+
}
126+
127+
// check method specific id
128+
if (parts[1].length() == 0) {
129+
throw new IllegalArgumentException("The URI [" + uri + "] is not valid DID, method specific id [" + parts[1] + "] is blank.");
119130
}
120131

121-
return new Did(parts[max - 3], version, methodSpecificId);
132+
return new Did(parts[0], parts[1]);
122133
}
134+
123135
public String getMethod() {
124136
return method;
125137
}
126138

127-
public String getVersion() {
128-
return version;
129-
}
130-
131139
public String getMethodSpecificId() {
132-
return methodSpecificId;
140+
return specificId;
133141
}
134142

135143
public URI toUri() {
136144
try {
137-
return new URI(SCHEME, method + ":" + methodSpecificId, null);
145+
return new URI(SCHEME, method + ":" + specificId, null);
138146
} catch (URISyntaxException e) {
139147
throw new IllegalStateException(e);
140148
}
@@ -150,23 +158,17 @@ public DidUrl asDidUrl() {
150158

151159
@Override
152160
public String toString() {
153-
final StringBuilder builder = new StringBuilder()
154-
.append(SCHEME)
155-
.append(':')
156-
.append(method)
157-
.append(':');
158-
159-
if (!"1".equals(version)) {
160-
builder
161-
.append(version)
162-
.append(':');
163-
}
164-
return builder.append(methodSpecificId).toString();
161+
return new StringBuilder()
162+
.append(SCHEME)
163+
.append(':')
164+
.append(method)
165+
.append(':')
166+
.append(specificId).toString();
165167
}
166168

167169
@Override
168170
public int hashCode() {
169-
return Objects.hash(method, methodSpecificId, version);
171+
return Objects.hash(method, specificId);
170172
}
171173

172174
@Override
@@ -181,16 +183,15 @@ public boolean equals(final Object obj) {
181183
return false;
182184
}
183185
Did other = (Did) obj;
184-
return Objects.equals(method, other.method) && Objects.equals(methodSpecificId, other.methodSpecificId)
185-
&& Objects.equals(version, other.version);
186+
return Objects.equals(method, other.method) && Objects.equals(specificId, other.specificId);
187+
186188
}
187-
189+
188190
static final boolean isNotBlank(String value) {
189191
return value != null && !value.trim().isEmpty();
190192
}
191193

192194
static final boolean isBlank(String value) {
193195
return value == null || value.trim().isEmpty();
194196
}
195-
196197
}

src/main/java/com/apicatalog/did/DidUrl.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
public class DidUrl extends Did {
1010

1111
private static final long serialVersionUID = 5752880077497569763L;
12-
12+
1313
protected final String path;
1414
protected final String query;
1515
protected final String fragment;
1616

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

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

3636
return new DidUrl(did, uri.getPath(), uri.getQuery(), uri.getFragment());
3737
}
@@ -56,7 +56,7 @@ public static boolean isDidUrl(final String uri) {
5656
@Override
5757
public URI toUri() {
5858
try {
59-
return new URI(SCHEME, method + ":" + methodSpecificId, path, query, fragment);
59+
return new URI(SCHEME, method + ":" + specificId, path, query, fragment);
6060
} catch (URISyntaxException e) {
6161
throw new IllegalStateException(e);
6262
}

src/main/java/com/apicatalog/did/key/DidKey.java

+29-11
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
/**
1010
* Immutable DID Key
1111
* <p>
12-
* did-key-format := did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type,
13-
* raw-public-key-bytes))
12+
* did-key-format := did:key:[version]:MULTIBASE(multiencodedKey)
1413
* </p>
1514
*
1615
* @see <a href=
@@ -20,18 +19,23 @@
2019
*/
2120
public class DidKey extends Did {
2221

23-
private static final long serialVersionUID = 3710900614215756688L;
22+
private static final long serialVersionUID = 1343361455801198884L;
2423

2524
public static final String METHOD_KEY = "key";
2625

26+
public static final String DEFAULT_VERSION = "1";
27+
28+
protected final String version;
29+
2730
protected final Multibase base;
2831

29-
protected final byte[] encodedKey;
32+
protected final byte[] debased;
3033

31-
protected DidKey(String version, String encoded, Multibase base, byte[] debased) {
32-
super(METHOD_KEY, version, encoded);
34+
protected DidKey(String version, String specificId, Multibase base, byte[] debased) {
35+
super(METHOD_KEY, specificId);
3336
this.base = base;
34-
this.encodedKey = debased;
37+
this.version = version;
38+
this.debased = debased;
3539
}
3640

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

66-
final Multibase base = bases.getBase(did.getMethodSpecificId()).orElseThrow(() -> new IllegalArgumentException("Unsupported did:key base encoding. DID [" + did.toString() + "]."));
70+
final String[] parts = did.getMethodSpecificId().split(":", 2);
71+
72+
String version = DEFAULT_VERSION;
73+
String encoded = parts[0];
74+
75+
if (parts.length == 2) {
76+
version = parts[0];
77+
encoded = parts[1];
78+
}
79+
80+
final Multibase base = bases.getBase(encoded).orElseThrow(() -> new IllegalArgumentException("Unsupported did:key base encoding. DID [" + did.toString() + "]."));
6781

68-
final byte[] debased = base.decode(did.getMethodSpecificId());
82+
final byte[] debased = base.decode(encoded);
6983

70-
return new DidKey(did.getVersion(), did.getMethodSpecificId(), base, debased);
84+
return new DidKey(version, encoded, base, debased);
7185
}
7286

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

95109
public byte[] getKey() {
96-
return encodedKey;
110+
return debased;
111+
}
112+
113+
public String getVersion() {
114+
return version;
97115
}
98116
}

0 commit comments

Comments
 (0)