mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-12-10 22:31:09 +01:00
WIP: Create HardwreKey/HardwareToken interfaces
This commit is contained in:
parent
84c30f9629
commit
5b55150234
5 changed files with 72 additions and 29 deletions
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.bouncycastle.openpgp.hardware
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single cryptographic key stored on a [HardwareToken].
|
||||||
|
*
|
||||||
|
* @param label 20 octets of label, such as the keys v4 fingerprint.
|
||||||
|
* @param identifier slot identifier
|
||||||
|
*/
|
||||||
|
open class HardwareKey<I>(val label: ByteArray, val identifier: I) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.bouncycastle.openpgp.hardware
|
||||||
|
|
||||||
|
interface HardwareToken {
|
||||||
|
|
||||||
|
val keys: Map<ByteArray, HardwareKey>
|
||||||
|
}
|
||||||
|
|
@ -10,20 +10,55 @@ import com.yubico.yubikit.management.DeviceInfo
|
||||||
import com.yubico.yubikit.openpgp.KeyRef
|
import com.yubico.yubikit.openpgp.KeyRef
|
||||||
import com.yubico.yubikit.openpgp.OpenPgpSession
|
import com.yubico.yubikit.openpgp.OpenPgpSession
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
|
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
|
||||||
|
import org.bouncycastle.openpgp.hardware.HardwareKey
|
||||||
|
import org.bouncycastle.openpgp.hardware.HardwareToken
|
||||||
import org.gnupg.GnuPGDummyKeyUtil
|
import org.gnupg.GnuPGDummyKeyUtil
|
||||||
|
|
||||||
data class Yubikey(val info: DeviceInfo, val device: YubiKeyDevice) {
|
data class Yubikey(val info: DeviceInfo, val device: YubiKeyDevice) : HardwareToken {
|
||||||
|
|
||||||
|
override val keys: Map<ByteArray, HardwareKey<KeyRef>>
|
||||||
|
get() = getFingerprints().values
|
||||||
|
.filterNotNull()
|
||||||
|
.associateWith { HardwareKey(it, keyRefForFingerprint(it)!!) }
|
||||||
|
|
||||||
|
fun openSession(): OpenPgpSession {
|
||||||
|
return OpenPgpSession(device.openConnection(SmartCardConnection::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun factoryReset() {
|
||||||
|
openSession().use {
|
||||||
|
it.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun storeKeyInSlot(key: OpenPGPPrivateKey, keyRef: KeyRef, adminPin: CharArray) {
|
fun storeKeyInSlot(key: OpenPGPPrivateKey, keyRef: KeyRef, adminPin: CharArray) {
|
||||||
device.openConnection(SmartCardConnection::class.java).use {
|
openSession().use {
|
||||||
val session = OpenPgpSession(it as SmartCardConnection)
|
|
||||||
|
|
||||||
// Storing keys requires admin pin
|
// Storing keys requires admin pin
|
||||||
session.verifyAdminPin(adminPin)
|
it.verifyAdminPin(adminPin)
|
||||||
|
|
||||||
session.writePrivateKey(key, keyRef)
|
it.writePrivateKey(key, keyRef)
|
||||||
session.writeFingerprint(key.publicKey, keyRef)
|
it.writeFingerprint(key.publicKey, keyRef)
|
||||||
session.writeGenerationTime(key.publicKey, keyRef)
|
it.writeGenerationTime(key.publicKey, keyRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun keyRefForFingerprint(fingerprint: ByteArray): KeyRef? {
|
||||||
|
return getFingerprints().entries
|
||||||
|
.find { it.value?.contentEquals(fingerprint) ?: false }
|
||||||
|
?.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFingerprints(): Map<KeyRef, ByteArray?> {
|
||||||
|
return openSession().use {
|
||||||
|
// session.getData(KeyRef.DEC.fingerprint)
|
||||||
|
val ddo = it.applicationRelatedData.discretionary
|
||||||
|
|
||||||
|
buildMap {
|
||||||
|
put(KeyRef.ATT, ddo.getFingerprint(KeyRef.ATT))
|
||||||
|
put(KeyRef.SIG, ddo.getFingerprint(KeyRef.SIG))
|
||||||
|
put(KeyRef.DEC, ddo.getFingerprint(KeyRef.DEC))
|
||||||
|
put(KeyRef.AUT, ddo.getFingerprint(KeyRef.AUT))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,9 @@ class YubikeyHardwareTokenBackend : HardwareTokenBackend {
|
||||||
|
|
||||||
override fun listKeyFingerprints(): Map<ByteArray, List<ByteArray>> {
|
override fun listKeyFingerprints(): Map<ByteArray, List<ByteArray>> {
|
||||||
return YubikeyHelper().listDevices().associate { yk ->
|
return YubikeyHelper().listDevices().associate { yk ->
|
||||||
yk.encodedSerialNumber to
|
yk.encodedSerialNumber to yk.openSession().use {
|
||||||
yk.device.openConnection(SmartCardConnection::class.java).use {
|
|
||||||
val session = OpenPgpSession(it)
|
|
||||||
// session.getData(KeyRef.DEC.fingerprint)
|
// session.getData(KeyRef.DEC.fingerprint)
|
||||||
val ddo = session.applicationRelatedData.discretionary
|
val ddo = it.applicationRelatedData.discretionary
|
||||||
|
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
ddo.getFingerprint(KeyRef.ATT),
|
ddo.getFingerprint(KeyRef.ATT),
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,9 @@
|
||||||
|
|
||||||
package org.pgpainless.yubikey
|
package org.pgpainless.yubikey
|
||||||
|
|
||||||
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
|
||||||
import com.yubico.yubikit.desktop.CompositeDevice
|
import com.yubico.yubikit.desktop.CompositeDevice
|
||||||
import com.yubico.yubikit.desktop.YubiKitManager
|
import com.yubico.yubikit.desktop.YubiKitManager
|
||||||
import com.yubico.yubikit.openpgp.KeyRef
|
import com.yubico.yubikit.openpgp.KeyRef
|
||||||
import com.yubico.yubikit.openpgp.OpenPgpSession
|
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
|
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
|
||||||
|
|
@ -28,20 +26,15 @@ class YubikeyHelper(private val api: PGPainless = PGPainless.getInstance()) {
|
||||||
.filter { it.key is CompositeDevice }
|
.filter { it.key is CompositeDevice }
|
||||||
.map { Yubikey(it.value, it.key) }
|
.map { Yubikey(it.value, it.key) }
|
||||||
} catch (e: RuntimeException) {
|
} catch (e: RuntimeException) {
|
||||||
|
// If there are no tokens, yubikit throws a RuntimeException :/
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun factoryReset(yubikey: Yubikey) {
|
|
||||||
yubikey.device.openConnection(SmartCardConnection::class.java).use {
|
|
||||||
OpenPgpSession(it).reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun moveToYubikey(
|
fun moveToYubikey(
|
||||||
componentKey: OpenPGPPrivateKey,
|
componentKey: OpenPGPPrivateKey,
|
||||||
yubikey: Yubikey,
|
yubikey: Yubikey,
|
||||||
adminPin: CharArray,
|
adminPin: CharArray,
|
||||||
keyRef: KeyRef = keyRefForKey(componentKey.publicKey)
|
keyRef: KeyRef = guessKeyRefForKey(componentKey.publicKey)
|
||||||
): OpenPGPKey {
|
): OpenPGPKey {
|
||||||
// Move private key to hardware token
|
// Move private key to hardware token
|
||||||
yubikey.storeKeyInSlot(componentKey, keyRef, adminPin)
|
yubikey.storeKeyInSlot(componentKey, keyRef, adminPin)
|
||||||
|
|
@ -58,7 +51,7 @@ class YubikeyHelper(private val api: PGPainless = PGPainless.getInstance()) {
|
||||||
.toOpenPGPKey(api.implementation)
|
.toOpenPGPKey(api.implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun keyRefForKey(key: OpenPGPComponentKey): KeyRef {
|
private fun guessKeyRefForKey(key: OpenPGPComponentKey): KeyRef {
|
||||||
return when {
|
return when {
|
||||||
key.isSigningKey -> KeyRef.SIG
|
key.isSigningKey -> KeyRef.SIG
|
||||||
key.isEncryptionKey -> KeyRef.DEC
|
key.isEncryptionKey -> KeyRef.DEC
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue