package com.intrbiz.bergamot.model; import java.io.Serializable; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.sql.Timestamp; import java.util.UUID; import com.intrbiz.Util; import com.intrbiz.bergamot.data.BergamotDB; import com.intrbiz.data.DataException; import com.intrbiz.data.db.compiler.meta.Action; import com.intrbiz.data.db.compiler.meta.SQLColumn; import com.intrbiz.data.db.compiler.meta.SQLForeignKey; import com.intrbiz.data.db.compiler.meta.SQLPrimaryKey; import com.intrbiz.data.db.compiler.meta.SQLTable; import com.intrbiz.data.db.compiler.meta.SQLVersion; import com.yubico.u2f.data.DeviceRegistration; import com.yubico.u2f.data.messages.key.util.CertificateParser; import com.yubico.u2f.data.messages.key.util.U2fB64Encoding; /** * A U2F device which has been registered to a user */ @SQLTable(schema = BergamotDB.class, name = "u2f_device_registration", since = @SQLVersion({ 3, 38, 0 })) public class ContactU2FDeviceRegistration implements Serializable { private static final long serialVersionUID = 1L; @SQLColumn(index = 1, name = "id", since = @SQLVersion({ 3,38, 0 })) @SQLPrimaryKey private UUID id; /** * The contact whom this token represents */ @SQLColumn(index = 2, name = "contact_id", since = @SQLVersion({ 3, 38, 0 })) @SQLForeignKey(references = Contact.class, on = "id", onDelete = Action.CASCADE, onUpdate = Action.RESTRICT, since = @SQLVersion({ 1, 0, 0 })) private UUID contactId; @SQLColumn(index = 3, name = "key_handle", notNull = true, since = @SQLVersion({ 3, 38, 0 })) private String keyHandle; @SQLColumn(index = 4, name = "public_key", notNull = true, since = @SQLVersion({ 3, 38, 0 })) private String publicKey; @SQLColumn(index = 5, name = "attestation_cert", since = @SQLVersion({ 3, 38, 0 })) private String attestationCert; @SQLColumn(index = 6, name = "counter", notNull = true, since = @SQLVersion({ 3, 38, 0 })) private long counter; /** * When did we register this device */ @SQLColumn(index = 7, name = "created", since = @SQLVersion({ 3, 38, 0 })) private Timestamp created = new Timestamp(System.currentTimeMillis()); /** * When was this last updated */ @SQLColumn(index = 8, name = "updated", since = @SQLVersion({ 3, 38, 0 })) private Timestamp updated = null; /** * Has this device been revoked */ @SQLColumn(index = 9, name = "revoked", since = @SQLVersion({ 3, 38, 0 })) private boolean revoked = false; /** * When was it revoked */ @SQLColumn(index = 10, name = "revoked_at", since = @SQLVersion({ 3, 38, 0 })) private Timestamp revokedAt = null; @SQLColumn(index = 11, name = "vendor", since = @SQLVersion({ 3, 38, 0 })) private String vendor; @SQLColumn(index = 12, name = "device", since = @SQLVersion({ 3, 38, 0 })) private String device; @SQLColumn(index = 13, name = "device_image", since = @SQLVersion({ 3, 38, 0 })) private String deviceImage; /** * A name for this method */ @SQLColumn(index = 14, name = "summary", since = @SQLVersion({ 3, 39, 0 })) private String summary; public ContactU2FDeviceRegistration() { super(); } public ContactU2FDeviceRegistration(Contact contact, DeviceRegistration devReg, String vendor, String device, String deviceImage, String name) { this.id = Site.randomId(contact.getSiteId()); this.contactId = contact.getId(); this.created = new Timestamp(System.currentTimeMillis()); this.updated = null; this.revoked = false; this.fromDeviceRegistration(devReg); this.vendor = vendor; this.device = device; this.deviceImage = deviceImage; this.summary = name; } public UUID getContactId() { return contactId; } public void setContactId(UUID contactId) { this.contactId = contactId; } public UUID getId() { return id; } public void setId(UUID id) { this.id = id; } public String getKeyHandle() { return keyHandle; } public void setKeyHandle(String keyHandle) { this.keyHandle = keyHandle; } public String getPublicKey() { return publicKey; } public void setPublicKey(String publicKey) { this.publicKey = publicKey; } public String getAttestationCert() { return attestationCert; } public void setAttestationCert(String attestationCert) { this.attestationCert = attestationCert; } public void saveAttestationCert(X509Certificate attestationCert) throws CertificateEncodingException { this.attestationCert = U2fB64Encoding.encode(attestationCert.getEncoded()); } public X509Certificate loadAttestationCert() throws CertificateException { if (Util.isEmpty(this.attestationCert)) return null; // parse the cert return CertificateParser.parseDer(U2fB64Encoding.decode(this.attestationCert)); } public long getCounter() { return counter; } public void setCounter(long counter) { this.counter = counter; } public Timestamp getCreated() { return created; } public void setCreated(Timestamp created) { this.created = created; } public Timestamp getUpdated() { return updated; } public void setUpdated(Timestamp updated) { this.updated = updated; } public boolean isRevoked() { return revoked; } public void setRevoked(boolean revoked) { this.revoked = revoked; } public Timestamp getRevokedAt() { return revokedAt; } public void setRevokedAt(Timestamp revokedAt) { this.revokedAt = revokedAt; } public String getVendor() { return vendor; } public void setVendor(String vendor) { this.vendor = vendor; } public String getDevice() { return device; } public void setDevice(String device) { this.device = device; } public String getDeviceImage() { return deviceImage; } public void setDeviceImage(String deviceImage) { this.deviceImage = deviceImage; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } public Contact getContact() { if (this.getContactId() == null) return null; try (BergamotDB db = BergamotDB.connect()) { return db.getContact(this.getContactId()); } } public void fromDeviceRegistration(DeviceRegistration dev) { this.setKeyHandle(dev.getKeyHandle()); this.setPublicKey(dev.getPublicKey()); this.setCounter(dev.getCounter()); try { this.saveAttestationCert(dev.getAttestationCertificate()); } catch (Exception e) { throw new DataException("Failed to save attestation certificate", e); } } public DeviceRegistration toDeviceRegistration() { try { DeviceRegistration dev = new DeviceRegistration(this.keyHandle, this.publicKey, this.loadAttestationCert(), this.counter); if (this.isRevoked()) dev.markCompromised(); return dev; } catch (Exception e) { throw new DataException("Failed to load attestation certificate", e); } } public ContactU2FDeviceRegistration revoke() { this.revoked = true; this.revokedAt = new Timestamp(System.currentTimeMillis()); return this; } public ContactU2FDeviceRegistration used(long newCounter) { this.counter = newCounter; this.updated = new Timestamp(System.currentTimeMillis()); return this; } public String toString() { return "U2FDevice { " + this.keyHandle + ", " + this.publicKey + "}"; } }