mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-12-09 22:01:10 +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.OpenPgpSession
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
|
||||
import org.bouncycastle.openpgp.hardware.HardwareKey
|
||||
import org.bouncycastle.openpgp.hardware.HardwareToken
|
||||
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) {
|
||||
device.openConnection(SmartCardConnection::class.java).use {
|
||||
val session = OpenPgpSession(it as SmartCardConnection)
|
||||
|
||||
openSession().use {
|
||||
// Storing keys requires admin pin
|
||||
session.verifyAdminPin(adminPin)
|
||||
it.verifyAdminPin(adminPin)
|
||||
|
||||
session.writePrivateKey(key, keyRef)
|
||||
session.writeFingerprint(key.publicKey, keyRef)
|
||||
session.writeGenerationTime(key.publicKey, keyRef)
|
||||
it.writePrivateKey(key, keyRef)
|
||||
it.writeFingerprint(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>> {
|
||||
return YubikeyHelper().listDevices().associate { yk ->
|
||||
yk.encodedSerialNumber to
|
||||
yk.device.openConnection(SmartCardConnection::class.java).use {
|
||||
val session = OpenPgpSession(it)
|
||||
yk.encodedSerialNumber to yk.openSession().use {
|
||||
// session.getData(KeyRef.DEC.fingerprint)
|
||||
val ddo = session.applicationRelatedData.discretionary
|
||||
val ddo = it.applicationRelatedData.discretionary
|
||||
|
||||
listOfNotNull(
|
||||
ddo.getFingerprint(KeyRef.ATT),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@
|
|||
|
||||
package org.pgpainless.yubikey
|
||||
|
||||
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
||||
import com.yubico.yubikit.desktop.CompositeDevice
|
||||
import com.yubico.yubikit.desktop.YubiKitManager
|
||||
import com.yubico.yubikit.openpgp.KeyRef
|
||||
import com.yubico.yubikit.openpgp.OpenPgpSession
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
|
||||
|
|
@ -28,20 +26,15 @@ class YubikeyHelper(private val api: PGPainless = PGPainless.getInstance()) {
|
|||
.filter { it.key is CompositeDevice }
|
||||
.map { Yubikey(it.value, it.key) }
|
||||
} catch (e: RuntimeException) {
|
||||
// If there are no tokens, yubikit throws a RuntimeException :/
|
||||
emptyList()
|
||||
}
|
||||
|
||||
fun factoryReset(yubikey: Yubikey) {
|
||||
yubikey.device.openConnection(SmartCardConnection::class.java).use {
|
||||
OpenPgpSession(it).reset()
|
||||
}
|
||||
}
|
||||
|
||||
fun moveToYubikey(
|
||||
componentKey: OpenPGPPrivateKey,
|
||||
yubikey: Yubikey,
|
||||
adminPin: CharArray,
|
||||
keyRef: KeyRef = keyRefForKey(componentKey.publicKey)
|
||||
keyRef: KeyRef = guessKeyRefForKey(componentKey.publicKey)
|
||||
): OpenPGPKey {
|
||||
// Move private key to hardware token
|
||||
yubikey.storeKeyInSlot(componentKey, keyRef, adminPin)
|
||||
|
|
@ -58,7 +51,7 @@ class YubikeyHelper(private val api: PGPainless = PGPainless.getInstance()) {
|
|||
.toOpenPGPKey(api.implementation)
|
||||
}
|
||||
|
||||
private fun keyRefForKey(key: OpenPGPComponentKey): KeyRef {
|
||||
private fun guessKeyRefForKey(key: OpenPGPComponentKey): KeyRef {
|
||||
return when {
|
||||
key.isSigningKey -> KeyRef.SIG
|
||||
key.isEncryptionKey -> KeyRef.DEC
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue