Skip to content

Commit 95db6fd

Browse files
committed
feat: add cosekey decode support
1 parent d46bdfa commit 95db6fd

File tree

5 files changed

+115
-20
lines changed

5 files changed

+115
-20
lines changed

inspector-vc/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,12 @@
9999
<artifactId>bcprov-jdk18on</artifactId>
100100
<version>1.80</version>
101101
</dependency>
102+
103+
<!-- Cose -->
104+
<dependency>
105+
<groupId>com.authlete</groupId>
106+
<artifactId>cbor</artifactId>
107+
<version>1.19</version>
108+
</dependency>
102109
</dependencies>
103110
</project>

inspector-vc/src/main/java/org/velocitynetwork/contracts/AesGcmEncryptor.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import javax.crypto.spec.GCMParameterSpec;
55
import javax.crypto.spec.PBEKeySpec;
66
import javax.crypto.spec.SecretKeySpec;
7-
import java.nio.charset.StandardCharsets;
87
import java.security.SecureRandom;
98

109
public class AesGcmEncryptor {
@@ -15,7 +14,7 @@ public class AesGcmEncryptor {
1514
private static final int KEY_LENGTH_BIT = 256;
1615
private static final int PBKDF2_ITERATIONS = 2145;
1716

18-
public static byte[] encrypt(String plaintext, String secret) throws Exception {
17+
public static byte[] encrypt(byte[] buffer, String secret) throws Exception {
1918
byte[] salt = new byte[SALT_LENGTH];
2019
byte[] iv = new byte[IV_LENGTH];
2120
SecureRandom random = new SecureRandom();
@@ -28,7 +27,7 @@ public static byte[] encrypt(String plaintext, String secret) throws Exception {
2827
GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH_BIT, iv);
2928
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
3029

31-
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
30+
byte[] ciphertext = cipher.doFinal(buffer);
3231
byte[] tag = cipher.getIV(); // tag is internally appended in GCM
3332
byte[] authTag = cipher.getParameters().getParameterSpec(GCMParameterSpec.class).getIV(); // not always reliable
3433

@@ -41,7 +40,7 @@ public static byte[] encrypt(String plaintext, String secret) throws Exception {
4140
return full;
4241
}
4342

44-
public static String decrypt(byte[] data, String secret) throws Exception {
43+
public static byte[] decrypt(byte[] data, String secret) throws Exception {
4544
byte[] salt = new byte[SALT_LENGTH];
4645
byte[] iv = new byte[IV_LENGTH];
4746
byte[] tag = new byte[TAG_LENGTH_BIT / 8];
@@ -59,7 +58,7 @@ public static String decrypt(byte[] data, String secret) throws Exception {
5958
cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec);
6059
cipher.updateAAD(tag); // Optional, depending on tagging mode
6160

62-
return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
61+
return cipher.doFinal(ciphertext);
6362
}
6463

6564
private static SecretKey deriveKey(String password, byte[] salt) throws Exception {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.velocitynetwork.contracts;
2+
3+
import com.authlete.cbor.*;
4+
import com.authlete.cose.COSEKey;
5+
6+
import java.util.Base64;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
public class COSERSAKey extends COSEKey {
11+
private byte[] n;
12+
private byte[] e;
13+
14+
public COSERSAKey(List<? extends CBORPair> pairs) {
15+
super(pairs);
16+
this.validateParameters(pairs);
17+
}
18+
19+
private void validateParameters(List<? extends CBORPair> pairs) {
20+
for(CBORPair pair : pairs) {
21+
this.validateParameter(pair);
22+
}
23+
}
24+
25+
private void validateParameter(CBORPair pair) {
26+
CBORItem label = pair.getKey();
27+
if (label instanceof CBORInteger) {
28+
this.validateKnownParameter((Integer)((CBORInteger)label).getValue(), pair.getValue());
29+
}
30+
}
31+
32+
private void validateKnownParameter(int label, CBORItem value) {
33+
switch (label) {
34+
case -1:
35+
this.n = validateN(value);
36+
break;
37+
case -2:
38+
this.e = validateE(value);
39+
break;
40+
default:
41+
throw new IllegalArgumentException("RSA private keys are not supported");
42+
}
43+
}
44+
45+
private static byte[] validateN(CBORItem value) {
46+
if (value instanceof CBORByteArray) {
47+
return (byte[])getRawValue(value);
48+
} else {
49+
throw new IllegalArgumentException("n (-1) must be a byte string.");
50+
}
51+
}
52+
53+
private static byte[] validateE(CBORItem value) {
54+
if (value instanceof CBORByteArray) {
55+
return (byte[])getRawValue(value);
56+
} else {
57+
throw new IllegalArgumentException("e (-1) must be a byte string.");
58+
}
59+
}
60+
61+
public boolean isPrivate() {
62+
return false;
63+
}
64+
65+
protected void addJwkProperties(Map<String, Object> map) {
66+
if (this.n != null) {
67+
map.put("x", encodeByBase64Url(this.n));
68+
}
69+
70+
if (this.e != null) {
71+
map.put("d", encodeByBase64Url(this.e));
72+
}
73+
}
74+
75+
static Object getRawValue(CBORItem item) {
76+
return ((CBORValue)item).getValue();
77+
}
78+
79+
static String encodeByBase64Url(byte[] bytes) {
80+
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
81+
}
82+
}

inspector-vc/src/main/java/org/velocitynetwork/contracts/VelocityNetworkDidResolver.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.velocitynetwork.contracts;
22

3+
import com.authlete.cbor.CBORDecoder;
4+
import com.authlete.cbor.CBORItem;
5+
import com.authlete.cose.COSEEC2Key;
6+
import com.authlete.cose.COSEKey;
37
import jakarta.json.Json;
48
import jakarta.json.JsonObject;
59
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
@@ -59,10 +63,14 @@ public static JsonObject resolvePublicKey(String id, VelocityNetworkMetadataRegi
5963
}
6064

6165
try {
62-
String publicKey = AesGcmEncryptor.decrypt(entry.encryptedPublicKey, secret);
63-
Secp256k1JWK publicKeyJwk = new Secp256k1JWK.Builder(fromRawPublicKey(publicKey.getBytes(StandardCharsets.UTF_8))).build();
66+
byte[] publicKeyBytes = AesGcmEncryptor.decrypt(entry.encryptedPublicKey, secret);
67+
CBORItem item = new CBORDecoder(publicKeyBytes).next();
68+
COSEKey coseKey = COSEKey.build(item);
69+
if ((Integer) coseKey.getKty() == 3) {
70+
coseKey = new COSERSAKey(coseKey.getPairs());
71+
}
6472
JsonObject issuerVcJwt = IssuerVc.deserializeIssuerVc(entry);
65-
return VerificationMethod.buildVerificationMethod(id + "#key-1", issuerVcJwt.getJsonObject("payload").getString("iss"), publicKeyJwk);
73+
return VerificationMethod.buildVerificationMethod(id + "#key-1", issuerVcJwt.getJsonObject("payload").getString("iss"), coseKey);
6674
} catch (Exception e) {
6775
return VerificationMethod.buildVerificationMethod(id);
6876
}
@@ -82,7 +90,7 @@ public VelocityNetworkDidResolver(String rpcUrl, String privateKey, String contr
8290

8391
public JsonObject resolveDid(String did) throws Exception {
8492
List<Tuple<VelocityNetworkMetadataRegistry.CredentialIdentifier, String>> parsedDid = parseVelocityV2Did(did);
85-
TransactionReceipt transactionReceipt= this.metadataRegistryContract.getPaidEntries(parsedDid.stream().map(tuple -> tuple.t1).toList(), randomUUID().toString(), burnerDid, burnerDid).send();
93+
TransactionReceipt transactionReceipt = this.metadataRegistryContract.getPaidEntries(parsedDid.stream().map(tuple -> tuple.t1).toList(), randomUUID().toString(), burnerDid, burnerDid).send();
8694
List<VelocityNetworkMetadataRegistry.CredentialMetadata> metadataEntries =
8795
VelocityNetworkMetadataRegistry.getGotCredentialMetadataEvents(transactionReceipt).getLast().credentialMetadataList;
8896

inspector-vc/src/main/java/org/velocitynetwork/contracts/VerificationMethod.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
package org.velocitynetwork.contracts;
22

3+
import com.authlete.cose.COSEKey;
34
import jakarta.json.Json;
45
import jakarta.json.JsonObject;
56
import jakarta.json.JsonObjectBuilder;
6-
import org.web3j.crypto.Secp256k1JWK;
7+
8+
import java.util.Map;
79

810
public class VerificationMethod {
9-
public static JsonObject buildVerificationMethod(String id, String controller, Secp256k1JWK publicKeyJwk) {
10-
JsonObjectBuilder publicKeyJwkJson = Json.createObjectBuilder()
11-
.add("kty", publicKeyJwk.getKty())
12-
.add("crv", publicKeyJwk.getCrv())
13-
.add("x", publicKeyJwk.getX())
14-
.add("y", publicKeyJwk.getY());
15-
if (publicKeyJwk.getD() != null) {
16-
publicKeyJwkJson.add("d", publicKeyJwk.getD());
17-
}
18-
return Json.createObjectBuilder().add("id", id).add("publicKeyJwk", publicKeyJwkJson).add("controller", controller).build();
11+
public static JsonObject buildVerificationMethod(String id, String controller, COSEKey coseKey) {
12+
Map<String, Object> jwk = coseKey.toJwk();
13+
JsonObjectBuilder jwkJson = Json.createObjectBuilder()
14+
.add("kty", (String) jwk.get("kty"))
15+
.add("n", (String) jwk.get("n"))
16+
.add("e", (String) jwk.get("e"));
17+
return Json.createObjectBuilder().add("id", id).add("publicKeyJwk", jwkJson).add("controller", controller).build();
1918
}
2019

2120
public static JsonObject buildVerificationMethod(String id) {

0 commit comments

Comments
 (0)