Skip to content

Commit

Permalink
Merge pull request #103 from rikard-sics/eddsa_key_parsing
Browse files Browse the repository at this point in the history
Support for parsing EdDSA-related key types from Java objects to OneKey
  • Loading branch information
ivajloip authored Apr 28, 2021
2 parents 0385f31 + 1f1994e commit e461e5e
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 1 deletion.
48 changes: 47 additions & 1 deletion src/main/java/COSE/ASN1.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ private static String tagToString(int tag) {
private static final byte[] BitStringTag = new byte[]{0x3};
private static final int IntegerTag = 2;

/**
* Determine if a specific OID is matching either Ed25519, Ed448, X25519 or X448.
*
* @param oid the OID to check
* @return if it is matching Ed25519, Ed448, X25519 or X448
*/
public static boolean isEdXOid(byte[] oid) {
return Arrays.equals(oid, ASN1.Oid_Ed25519) || Arrays.equals(oid, ASN1.Oid_Ed448)
|| Arrays.equals(oid, ASN1.Oid_X25519) || Arrays.equals(oid, ASN1.Oid_X448);
}

/**
* Encode a subject public key info structure from an OID and the data bytes
* for the key
Expand Down Expand Up @@ -251,6 +262,33 @@ public static TagValue DecodeCompound(int offset, byte[] encoding) throws CoseEx
return new TagValue(retTag, result);
}

/**
* Decode an array of bytes which is supposed to be an ASN.1 encoded octet string.
*
* @param offset - starting offset in array to begin decoding
* @param encoding - bytes of the ASN.1 encoded value
* @return Decoded structure
* @throws CoseException - ASN.1 encoding errors
*/
public static TagValue DecodeSimple(int offset, byte[] encoding) throws CoseException {
ArrayList<TagValue> result = new ArrayList<TagValue>();
int retTag = encoding[offset];

if (encoding[offset] != 0x04)
throw new CoseException("Invalid structure");
int[] l = DecodeLength(offset + 1, encoding);

int sequenceLength = l[1];
if (offset + 2 + sequenceLength != encoding.length)
throw new CoseException("Invalid sequence");

int tag = encoding[offset];
offset += 1 + l[0];
result.add(new TagValue(tag, Arrays.copyOfRange(encoding, offset, offset + l[1])));

return new TagValue(retTag, result);
}

/**
* Encode a private key into a PKCS#8 private key structure.
*
Expand Down Expand Up @@ -371,8 +409,16 @@ public static ArrayList<TagValue> DecodePKCS8EC(ArrayList<TagValue> pkcs8) throw
// Decode the contents of the octet string PrivateKey

byte[] pk = pkcs8.get(2).value;
TagValue pkd;

TagValue pkd = DecodeCompound(0, pk);
// First check if it can be decoded as a simple value
if (pk[0] == 0x04) { // ASN.1 Octet string
pkd = DecodeSimple(0, pk);
return pkd.list;
}

// Otherwise proceed to parse as compound value
pkd = DecodeCompound(0, pk);
ArrayList<TagValue> pkdl = pkd.list;
if (pkd.tag != 0x30) throw new CoseException("Invalid ECPrivateKey");
if (pkdl.size() < 2 || pkdl.size() > 4) throw new CoseException("Invalid ECPrivateKey");
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/COSE/OneKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,30 @@ else if (Arrays.equals(alg.get(0).value, ASN1.Oid_rsaEncryption)) {

keyMap.Add(KeyKeys.RSA_N.AsCBOR(), n.value);
keyMap.Add(KeyKeys.RSA_E.AsCBOR(), e.value);
} else if (ASN1.isEdXOid(alg.get(0).value)) {
byte[] oid = (byte[]) alg.get(0).value;
if (oid == null)
throw new CoseException("Invalid SPKI structure");

// OKP Key
keyMap.Add(KeyKeys.KeyType.AsCBOR(), KeyKeys.KeyType_OKP);
keyMap.Add(KeyKeys.Algorithm.AsCBOR(), AlgorithmID.EDDSA.AsCBOR());
if (Arrays.equals(oid, ASN1.Oid_X25519))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_X25519);
else if (Arrays.equals(oid, ASN1.Oid_X448))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_X448);
else if (Arrays.equals(oid, ASN1.Oid_Ed25519))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_Ed25519);
else if (Arrays.equals(oid, ASN1.Oid_Ed448))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_Ed448);
else
throw new CoseException("Unsupported curve");

byte[] keyData = (byte[]) spki.get(1).value;
if (keyData[0] == 0) {
keyMap.Add(KeyKeys.OKP_X.AsCBOR(), Arrays.copyOfRange(keyData, 1, keyData.length));
} else
throw new CoseException("Invalid key data");
}
else {
throw new CoseException("Unsupported Algorithm");
Expand Down Expand Up @@ -162,6 +186,36 @@ else if (Arrays.equals(alg.get(0).value, ASN1.Oid_rsaEncryption)) {
keyMap.Add(KeyKeys.RSA_QI.AsCBOR(), pkdl.get(8).value);

// todo multi prime keys
} else if (ASN1.isEdXOid(alg.get(0).value)) {
byte[] oid = (byte[]) alg.get(0).value;
if (oid == null)
throw new CoseException("Invalid PKCS8 structure");
// OKP Key
if (!keyMap.ContainsKey(KeyKeys.KeyType.AsCBOR())) {

keyMap.Add(KeyKeys.Algorithm.AsCBOR(), AlgorithmID.EDDSA.AsCBOR());
if (Arrays.equals(oid, ASN1.Oid_X25519))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_X25519);
else if (Arrays.equals(oid, ASN1.Oid_X448))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_X448);
else if (Arrays.equals(oid, ASN1.Oid_Ed25519))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_Ed25519);
else if (Arrays.equals(oid, ASN1.Oid_Ed448))
keyMap.Add(KeyKeys.OKP_Curve.AsCBOR(), KeyKeys.OKP_Ed448);
else
throw new CoseException("Unsupported curve");

} else {
if (!this.get(KeyKeys.KeyType).equals(KeyKeys.KeyType_OKP)) {
throw new CoseException("Public/Private key don't match");
}
}

ArrayList<ASN1.TagValue> pkdl = ASN1.DecodePKCS8EC(pkl);
if (pkdl.get(0).tag != 4)
throw new CoseException("Invalid PKCS8 structure");
byte[] keyData = (byte[]) (pkdl.get(0).value);
keyMap.Add(KeyKeys.OKP_D.AsCBOR(), keyData);
}
else {
throw new CoseException("Unsupported Algorithm");
Expand Down

0 comments on commit e461e5e

Please sign in to comment.