Skip to content

Commit

Permalink
Extract function WebAuthnCodecs.importCosePublicKey
Browse files Browse the repository at this point in the history
  • Loading branch information
emlun committed Apr 17, 2019
1 parent c055550 commit 181f969
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,18 @@ public static ByteArray ecPublicKeyToCose(ECPublicKey key) {
return rawEcdaKeyToCose(ecPublicKeyToRaw(key));
}

public static ECPublicKey importCoseP256PublicKey(ByteArray key) throws CoseException, IOException {
return new COSE.ECPublicKey(new OneKey(CBORObject.DecodeFromBytes(key.getBytes())));
public static PublicKey importCosePublicKey(ByteArray key) throws CoseException, IOException {
CBORObject cose = CBORObject.DecodeFromBytes(key.getBytes());
final int kty = cose.get(CBORObject.FromObject(1)).AsInt32();
switch (kty) {
case 2: return importCoseP256PublicKey(cose);
default:
throw new IllegalArgumentException("Unsupported key type: " + kty);
}
}

private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException, IOException {
return new COSE.ECPublicKey(new OneKey(cose));
}

public static String getSignatureAlgorithmName(PublicKey key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,32 @@ public boolean verifySignature(X509Certificate attestationCertificate, ByteArray
return verifySignature(attestationCertificate.getPublicKey(), signedBytes, signature);
}

public boolean verifySignature(PublicKey publicKey, ByteArray signedBytes, ByteArray signature) {
public boolean verifySignature(PublicKey publicKey, ByteArray signedBytes, ByteArray signatureBytes) {
try {
Signature ecdsaSignature = Signature.getInstance("SHA256withECDSA", provider);
ecdsaSignature.initVerify(publicKey);
ecdsaSignature.update(signedBytes.getBytes());
return ecdsaSignature.verify(signature.getBytes());
} catch (GeneralSecurityException e) {
final String algName;
switch (publicKey.getAlgorithm()) {
case "EC":
algName = "SHA256withECDSA";
break;

case "RSA":
algName = "SHA256withRSA";
break;

default:
throw new IllegalArgumentException("Unsupported public key algorithm: " + publicKey);
}
Signature signature = Signature.getInstance(algName, provider);
signature.initVerify(publicKey);
signature.update(signedBytes.getBytes());
return signature.verify(signatureBytes.getBytes());
} catch (GeneralSecurityException | IllegalArgumentException e) {
throw new RuntimeException(
String.format(
"Failed to verify signature. This could be a problem with your JVM environment, or a bug in webauthn-server-core. Public key: %s, signed data: %s , signature: %s",
publicKey,
signedBytes.getBase64Url(),
signature.getBase64Url()
signatureBytes.getBase64Url()
),
e
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@
import COSE.CoseException;
import com.fasterxml.jackson.databind.JsonNode;
import com.yubico.internal.util.WebAuthnCodecs;
import com.yubico.webauthn.data.AttestedCredentialData;
import com.yubico.webauthn.data.AttestationObject;
import com.yubico.webauthn.data.AttestationType;
import com.yubico.webauthn.data.AttestedCredentialData;
import com.yubico.webauthn.data.ByteArray;
import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
Expand Down Expand Up @@ -80,17 +81,27 @@ private static boolean validSelfSignature(X509Certificate cert) {
}
}

private static ByteArray getRawUserPublicKey(AttestationObject attestationObject) throws IOException, CoseException {
final ByteArray pubkeyCose = attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getCredentialPublicKey();
final PublicKey pubkey = WebAuthnCodecs.importCosePublicKey(pubkeyCose);

final ECPublicKey ecPubkey;
try {
ecPubkey = (ECPublicKey) pubkey;
} catch (ClassCastException e) {
throw new RuntimeException( "U2F supports only EC keys, was: " + pubkey);
}

return WebAuthnCodecs.ecPublicKeyToRaw(ecPubkey);
}

@Override
public AttestationType getAttestationType(AttestationObject attestationObject) throws CoseException, IOException, CertificateException {
X509Certificate attestationCertificate = getAttestationCertificate(attestationObject);

if (attestationCertificate.getPublicKey() instanceof ECPublicKey
&& validSelfSignature(attestationCertificate)
&& WebAuthnCodecs.ecPublicKeyToRaw(
WebAuthnCodecs.importCoseP256PublicKey(
attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getCredentialPublicKey()
)
)
&& getRawUserPublicKey(attestationObject)
.equals(
WebAuthnCodecs.ecPublicKeyToRaw((ECPublicKey) attestationCertificate.getPublicKey())
)
Expand Down Expand Up @@ -128,14 +139,10 @@ && isP256(((ECPublicKey) attestationCertificate.getPublicKey()).getParams())
}

if (signature.isBinary()) {
ByteArray userPublicKey;
final ByteArray userPublicKey;

try {
userPublicKey = WebAuthnCodecs.ecPublicKeyToRaw(
WebAuthnCodecs.importCoseP256PublicKey(
attestedCredentialData.getCredentialPublicKey()
)
);
userPublicKey = getRawUserPublicKey(attestationObject);
} catch (IOException | CoseException e) {
RuntimeException err = new RuntimeException(String.format("Failed to parse public key from attestation data %s", attestedCredentialData));
log.error(err.getMessage(), err);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ public void validate() {
final PublicKey key;

try {
key = WebAuthnCodecs.importCoseP256PublicKey(cose);
key = WebAuthnCodecs.importCosePublicKey(cose);
} catch (CoseException | IOException e) {
throw new IllegalArgumentException(
String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private boolean verifyEcdaaSignature(AttestationObject attestationObject, ByteAr
private boolean verifySelfAttestationSignature(AttestationObject attestationObject, ByteArray clientDataJsonHash) {
final PublicKey pubkey;
try {
pubkey = WebAuthnCodecs.importCoseP256PublicKey(
pubkey = WebAuthnCodecs.importCosePublicKey(
attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getCredentialPublicKey()
);
} catch (IOException | CoseException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ object TestAuthenticator {
authData.getRpIdHash,
clientDataJson,
authData.getAttestedCredentialData.get.getCredentialId,
WebAuthnCodecs.ecPublicKeyToRaw(WebAuthnCodecs.importCoseP256PublicKey(authData.getAttestedCredentialData.get.getCredentialPublicKey))
WebAuthnCodecs.ecPublicKeyToRaw(WebAuthnCodecs.importCosePublicKey(authData.getAttestedCredentialData.get.getCredentialPublicKey).asInstanceOf[ECPublicKey])
)

val f = JsonNodeFactory.instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class WebAuthnCodecsSpec extends FunSpec with Matchers with GeneratorDrivenProp

val coseKey = WebAuthnCodecs.rawEcdaKeyToCose(rawKey)

val importedPubkey: ECPublicKey = WebAuthnCodecs.importCoseP256PublicKey(coseKey)
val importedPubkey: ECPublicKey = WebAuthnCodecs.importCosePublicKey(coseKey).asInstanceOf[ECPublicKey]
val rawImportedPubkey = WebAuthnCodecs.ecPublicKeyToRaw(importedPubkey)

rawImportedPubkey should equal (rawKey)
Expand All @@ -100,7 +100,7 @@ class WebAuthnCodecsSpec extends FunSpec with Matchers with GeneratorDrivenProp

val coseKey = WebAuthnCodecs.ecPublicKeyToCose(originalPubkey)

val importedPubkey: ECPublicKey = WebAuthnCodecs.importCoseP256PublicKey(coseKey)
val importedPubkey: ECPublicKey = WebAuthnCodecs.importCosePublicKey(coseKey).asInstanceOf[ECPublicKey]
val rawImportedPubkey = WebAuthnCodecs.ecPublicKeyToRaw(importedPubkey)

rawImportedPubkey should equal (rawKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

package com.yubico.webauthn.data

import java.security.interfaces.ECPublicKey

import com.upokecenter.cbor.CBORObject
import com.yubico.internal.util.WebAuthnCodecs
import com.yubico.internal.util.scala.JavaConverters._
Expand Down Expand Up @@ -71,7 +73,7 @@ class AuthenticatorDataSpec extends FunSpec with Matchers {
authData.getAttestedCredentialData.get.getAaguid.getHex should equal ("000102030405060708090a0b0c0d0e0f")
authData.getAttestedCredentialData.get.getCredentialId.getHex should equal ("7137c4e57894dce742723f9966c1e71c7c966f14e9429d5b2a2098a68416deec")

val pubkey: ByteArray = WebAuthnCodecs.ecPublicKeyToRaw(WebAuthnCodecs.importCoseP256PublicKey(authData.getAttestedCredentialData.get.getCredentialPublicKey))
val pubkey: ByteArray = WebAuthnCodecs.ecPublicKeyToRaw(WebAuthnCodecs.importCosePublicKey(authData.getAttestedCredentialData.get.getCredentialPublicKey).asInstanceOf[ECPublicKey])
pubkey should equal (ByteArray.fromHex("04DAFE0DE5312BA080A5CCDF6B483B10EF19A2454D1E17A8350311A0B7FF0566EF8EC6324D2C81398D2E80BC985B910B26970A0F408C9DE19BECCF39899A41674D"))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ object Test extends App {
val parsedAttObj = new AttestationObject(attestationObject)
println(parsedAttObj)
println(parsedAttObj.getAuthenticatorData.getBytes.getHex)
println(WebAuthnCodecs.importCoseP256PublicKey(parsedAttObj.getAuthenticatorData.getAttestedCredentialData.get.getCredentialPublicKey))
println(WebAuthnCodecs.importCosePublicKey(parsedAttObj.getAuthenticatorData.getAttestedCredentialData.get.getCredentialPublicKey))

val attestationObjectCbor = WebAuthnCodecs.cbor.readTree(attestationObject.getBytes)
println(attestationObjectCbor)
Expand Down

0 comments on commit 181f969

Please sign in to comment.