-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added wrapper to ensure compatibility with attestation.id main branch
- Loading branch information
Showing
8 changed files
with
204 additions
and
133 deletions.
There are no files selected for viewing
130 changes: 130 additions & 0 deletions
130
src/main/java/org/devcon/ticket/DevconTicketDecoder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package org.devcon.ticket; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.bouncycastle.asn1.ASN1BitString; | ||
import org.bouncycastle.asn1.ASN1Encodable; | ||
import org.bouncycastle.asn1.ASN1InputStream; | ||
import org.bouncycastle.asn1.ASN1Integer; | ||
import org.bouncycastle.asn1.ASN1OctetString; | ||
import org.bouncycastle.asn1.ASN1Primitive; | ||
import org.bouncycastle.asn1.ASN1Sequence; | ||
import org.bouncycastle.asn1.ASN1UTF8String; | ||
import org.bouncycastle.asn1.DERBitString; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | ||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter; | ||
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; | ||
import org.tokenscript.attestation.ObjectDecoder; | ||
import org.tokenscript.attestation.core.ExceptionUtil; | ||
import org.tokenscript.attestation.core.SignatureUtility; | ||
|
||
public class DevconTicketDecoder implements ObjectDecoder<Ticket> { | ||
private static final Logger logger = LogManager.getLogger(DevconTicketDecoder.class); | ||
private static final String DEFAULT = "default"; | ||
|
||
private Map<String, AsymmetricKeyParameter> idsToKeys; | ||
|
||
public DevconTicketDecoder(Map<String, AsymmetricKeyParameter> idsToKeys) { | ||
this.idsToKeys = idsToKeys; | ||
} | ||
|
||
public DevconTicketDecoder(AsymmetricKeyParameter publicKey) { | ||
this(); | ||
idsToKeys.put(DEFAULT, publicKey); | ||
} | ||
|
||
public DevconTicketDecoder() { | ||
idsToKeys = new HashMap<>(); | ||
} | ||
|
||
@Override | ||
public Ticket decode(byte[] encoding) throws IOException { | ||
ASN1InputStream input = null; | ||
try { | ||
input = new ASN1InputStream(encoding); | ||
ASN1Sequence asn1 = ASN1Sequence.getInstance(input.readObject()); | ||
input.close(); | ||
ASN1Sequence ticket = ASN1Sequence.getInstance(asn1.getObjectAt(0)); | ||
String devconId = (ASN1UTF8String.getInstance(ticket.getObjectAt(0))).getString(); | ||
ASN1Primitive ticketIdObj = ticket.getObjectAt(1).toASN1Primitive(); | ||
String ticketId; | ||
if (ticketIdObj instanceof ASN1Integer) { | ||
ticketId = (ASN1Integer.getInstance(ticket.getObjectAt(1))).getValue().toString(); | ||
} else { // ASN1UTF8String | ||
ticketId = (ASN1UTF8String.getInstance(ticket.getObjectAt(1))).getString(); | ||
} | ||
int ticketClassInt = ASN1Integer.getInstance(ticket.getObjectAt(2)).getValue().intValueExact(); | ||
byte[] commitment = (ASN1OctetString.getInstance(ticket.getObjectAt(3))).getOctets(); | ||
/* refactored 2021-01-05 : we don't care about the ticket class set on our level | ||
TicketClass ticketClass = null; | ||
for (TicketClass current : TicketClass.values()) { | ||
if (current.getValue() == ticketClassInt) { | ||
ticketClass = current; | ||
} | ||
} | ||
if (ticketClass == null) { | ||
throw new IOException("Not valid ticket class"); | ||
} | ||
*/ | ||
byte[] signature = parsePKandSignature(asn1, devconId, 1); | ||
return new Ticket(devconId, ticketId, ticketClassInt, commitment, signature, getPk(devconId)); | ||
} finally { | ||
input.close(); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the signature and ensures that the optional public key is properly restored | ||
* @param input The encoded Ticket | ||
* @return | ||
*/ | ||
byte[] parsePKandSignature(ASN1Sequence input, String devconId, int asnBaseIdx) throws IOException, IllegalArgumentException{ | ||
byte[] signature; | ||
ASN1Encodable object = input.getObjectAt(asnBaseIdx); | ||
if (object instanceof ASN1Sequence) { | ||
// The optional PublicKeyInfo is included | ||
parseEncodingOfPKInfo((ASN1Sequence) object, devconId); | ||
signature = ASN1BitString.getInstance(input.getObjectAt(asnBaseIdx+1)).getBytes(); | ||
} else if (object instanceof DERBitString) { | ||
// Only the signature is included | ||
signature = ASN1BitString.getInstance(input.getObjectAt(asnBaseIdx)).getBytes(); | ||
} else { | ||
throw ExceptionUtil.throwException(logger, | ||
new IllegalArgumentException("Invalid ticket encoding")); | ||
} | ||
return signature; | ||
} | ||
|
||
void parseEncodingOfPKInfo(ASN1Sequence publicKeyInfo, String devconId) throws IOException, IllegalArgumentException { | ||
AlgorithmIdentifier algorithm = AlgorithmIdentifier.getInstance(publicKeyInfo.getObjectAt(0)); | ||
byte[] publicKeyBytes = ASN1BitString.getInstance(publicKeyInfo.getObjectAt(1)).getEncoded(); | ||
AsymmetricKeyParameter decodedPublicKey = SignatureUtility.restoreDefaultKey(algorithm, publicKeyBytes); | ||
SubjectPublicKeyInfo decodedSpki = SubjectPublicKeyInfoFactory | ||
.createSubjectPublicKeyInfo(decodedPublicKey); | ||
// Ensure that the right type of public key is given | ||
if (getPk(devconId) != null) { | ||
SubjectPublicKeyInfo referenceSpki = SubjectPublicKeyInfoFactory | ||
.createSubjectPublicKeyInfo(getPk(devconId)); | ||
if (!Arrays.equals(referenceSpki.getEncoded(), decodedSpki.getEncoded())) { | ||
throw ExceptionUtil.throwException(logger, new IllegalArgumentException( | ||
"The public key is not of the same as supplied as argument")); | ||
} | ||
} | ||
idsToKeys.put(devconId, decodedPublicKey); | ||
} | ||
|
||
AsymmetricKeyParameter getPk(String devconId) { | ||
AsymmetricKeyParameter pk; | ||
if (idsToKeys.get(devconId) != null) { | ||
pk = idsToKeys.get(devconId); | ||
} else { | ||
pk = idsToKeys.get(DEFAULT); | ||
} | ||
return pk; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,130 +1,43 @@ | ||
package org.devcon.ticket; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.bouncycastle.asn1.ASN1BitString; | ||
import org.bouncycastle.asn1.ASN1Encodable; | ||
import org.bouncycastle.asn1.ASN1InputStream; | ||
import org.bouncycastle.asn1.ASN1Integer; | ||
import org.bouncycastle.asn1.ASN1OctetString; | ||
import org.bouncycastle.asn1.ASN1Primitive; | ||
import org.bouncycastle.asn1.ASN1Sequence; | ||
import org.bouncycastle.asn1.ASN1UTF8String; | ||
import org.bouncycastle.asn1.DERBitString; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | ||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter; | ||
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; | ||
import org.tokenscript.attestation.ObjectDecoder; | ||
import org.tokenscript.attestation.core.ExceptionUtil; | ||
import org.tokenscript.attestation.core.SignatureUtility; | ||
|
||
public class TicketDecoder implements ObjectDecoder<Ticket> { | ||
/** | ||
* Wrapper class that allows decoding of either Devcon or Liscon tickets. | ||
* This class only exists for backward compatibility reasons | ||
*/ | ||
@Deprecated | ||
public class TicketDecoder implements ObjectDecoder<Ticket> { | ||
private static final Logger logger = LogManager.getLogger(TicketDecoder.class); | ||
private static final String DEFAULT = "default"; | ||
|
||
private Map<String, AsymmetricKeyParameter> idsToKeys; | ||
private final List<ObjectDecoder<Ticket>> decoders = new ArrayList<>(2); | ||
|
||
public TicketDecoder(Map<String, AsymmetricKeyParameter> idsToKeys) { | ||
this.idsToKeys = idsToKeys; | ||
decoders.add(new DevconTicketDecoder(idsToKeys)); | ||
} | ||
|
||
public TicketDecoder(AsymmetricKeyParameter publicKey) { | ||
this(); | ||
idsToKeys.put(DEFAULT, publicKey); | ||
} | ||
|
||
public TicketDecoder() { | ||
idsToKeys = new HashMap<>(); | ||
decoders.add(new DevconTicketDecoder(publicKey)); | ||
decoders.add(new LisconTicketDecoder(publicKey)); | ||
} | ||
|
||
@Override | ||
public Ticket decode(byte[] encoding) throws IOException { | ||
ASN1InputStream input = null; | ||
try { | ||
input = new ASN1InputStream(encoding); | ||
ASN1Sequence asn1 = ASN1Sequence.getInstance(input.readObject()); | ||
input.close(); | ||
ASN1Sequence ticket = ASN1Sequence.getInstance(asn1.getObjectAt(0)); | ||
String devconId = (ASN1UTF8String.getInstance(ticket.getObjectAt(0))).getString(); | ||
ASN1Primitive ticketIdObj = ticket.getObjectAt(1).toASN1Primitive(); | ||
String ticketId; | ||
if (ticketIdObj instanceof ASN1Integer) { | ||
ticketId = (ASN1Integer.getInstance(ticket.getObjectAt(1))).getValue().toString(); | ||
} else { // ASN1UTF8String | ||
ticketId = (ASN1UTF8String.getInstance(ticket.getObjectAt(1))).getString(); | ||
} | ||
int ticketClassInt = ASN1Integer.getInstance(ticket.getObjectAt(2)).getValue().intValueExact(); | ||
byte[] commitment = (ASN1OctetString.getInstance(ticket.getObjectAt(3))).getOctets(); | ||
/* refactored 2021-01-05 : we don't care about the ticket class set on our level | ||
TicketClass ticketClass = null; | ||
for (TicketClass current : TicketClass.values()) { | ||
if (current.getValue() == ticketClassInt) { | ||
ticketClass = current; | ||
} | ||
for (ObjectDecoder<Ticket> currentDecoder : decoders) { | ||
try { | ||
return currentDecoder.decode(encoding); | ||
} catch (Exception e) { | ||
continue; | ||
} | ||
if (ticketClass == null) { | ||
throw new IOException("Not valid ticket class"); | ||
} | ||
*/ | ||
byte[] signature = parsePKandSignature(asn1, devconId, 1); | ||
return new Ticket(devconId, ticketId, ticketClassInt, commitment, signature, getPk(devconId)); | ||
} finally { | ||
input.close(); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the signature and ensures that the optional public key is properly restored | ||
* @param input The encoded Ticket | ||
* @return | ||
*/ | ||
byte[] parsePKandSignature(ASN1Sequence input, String devconId, int asnBaseIdx) throws IOException, IllegalArgumentException{ | ||
byte[] signature; | ||
ASN1Encodable object = input.getObjectAt(asnBaseIdx); | ||
if (object instanceof ASN1Sequence) { | ||
// The optional PublicKeyInfo is included | ||
parseEncodingOfPKInfo((ASN1Sequence) object, devconId); | ||
signature = ASN1BitString.getInstance(input.getObjectAt(asnBaseIdx+1)).getBytes(); | ||
} else if (object instanceof DERBitString) { | ||
// Only the signature is included | ||
signature = ASN1BitString.getInstance(input.getObjectAt(asnBaseIdx)).getBytes(); | ||
} else { | ||
throw ExceptionUtil.throwException(logger, | ||
new IllegalArgumentException("Invalid ticket encoding")); | ||
} | ||
return signature; | ||
} | ||
|
||
void parseEncodingOfPKInfo(ASN1Sequence publicKeyInfo, String devconId) throws IOException, IllegalArgumentException { | ||
AlgorithmIdentifier algorithm = AlgorithmIdentifier.getInstance(publicKeyInfo.getObjectAt(0)); | ||
byte[] publicKeyBytes = ASN1BitString.getInstance(publicKeyInfo.getObjectAt(1)).getEncoded(); | ||
AsymmetricKeyParameter decodedPublicKey = SignatureUtility.restoreDefaultKey(algorithm, publicKeyBytes); | ||
SubjectPublicKeyInfo decodedSpki = SubjectPublicKeyInfoFactory | ||
.createSubjectPublicKeyInfo(decodedPublicKey); | ||
// Ensure that the right type of public key is given | ||
if (getPk(devconId) != null) { | ||
SubjectPublicKeyInfo referenceSpki = SubjectPublicKeyInfoFactory | ||
.createSubjectPublicKeyInfo(getPk(devconId)); | ||
if (!Arrays.equals(referenceSpki.getEncoded(), decodedSpki.getEncoded())) { | ||
throw ExceptionUtil.throwException(logger, new IllegalArgumentException( | ||
"The public key is not of the same as supplied as argument")); | ||
} | ||
} | ||
idsToKeys.put(devconId, decodedPublicKey); | ||
} | ||
|
||
AsymmetricKeyParameter getPk(String devconId) { | ||
AsymmetricKeyParameter pk; | ||
if (idsToKeys.get(devconId) != null) { | ||
pk = idsToKeys.get(devconId); | ||
} else { | ||
pk = idsToKeys.get(DEFAULT); | ||
} | ||
return pk; | ||
ExceptionUtil.throwException(logger, new RuntimeException("Could not decode ticket")); | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.