mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 02:09:38 +02:00
Sketch out yubikey-based public key decryption.
Note: Requires a modified copy of Bouncy Castle with the following changes: * BcPublicKeyDataDecryptorFactory.unwrapSessionData(): make public * org.bouncycastle.openpgp.operator.bc.RFC6637KDFCalculator: Make class public
This commit is contained in:
parent
9f0a9ccfa6
commit
b0c3533b8c
1 changed files with 61 additions and 17 deletions
|
@ -1,24 +1,47 @@
|
|||
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.yubikey
|
||||
|
||||
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
||||
import com.yubico.yubikit.openpgp.KeyRef
|
||||
import com.yubico.yubikit.openpgp.OpenPgpSession
|
||||
import org.bouncycastle.bcpg.ECDHPublicBCPGKey
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags
|
||||
import org.bouncycastle.crypto.params.KeyParameter
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.operator.PGPPad
|
||||
import org.bouncycastle.openpgp.operator.RFC6637Utils
|
||||
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory
|
||||
import org.bouncycastle.openpgp.operator.bc.RFC6637KDFCalculator
|
||||
import org.pgpainless.decryption_verification.HardwareSecurity
|
||||
|
||||
class YubikeyDataDecryptorFactory(
|
||||
smartCardConnection: SmartCardConnection,
|
||||
callback: HardwareSecurity.DecryptionCallback,
|
||||
keyIdentifier: KeyIdentifier,
|
||||
) : HardwareSecurity.HardwareDataDecryptorFactory(keyIdentifier, callback) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun createDecryptorFromConnection(smartCardConnection: SmartCardConnection): HardwareSecurity.HardwareDataDecryptorFactory {
|
||||
fun createDecryptorFromConnection(
|
||||
smartCardConnection: SmartCardConnection,
|
||||
pubkey: PGPPublicKey
|
||||
): HardwareSecurity.HardwareDataDecryptorFactory {
|
||||
val openpgpSession = OpenPgpSession(smartCardConnection)
|
||||
val fingerprintBytes = openpgpSession.getData(KeyRef.DEC.fingerprint)
|
||||
val decKeyIdentifier = KeyIdentifier(fingerprintBytes)
|
||||
val rsa = true
|
||||
|
||||
if (!decKeyIdentifier.matches(pubkey.keyIdentifier)) {
|
||||
throw IllegalArgumentException("Fingerprint mismatch.")
|
||||
}
|
||||
|
||||
val isRSAKey = pubkey.algorithm == PublicKeyAlgorithmTags.RSA_GENERAL
|
||||
|| pubkey.algorithm == PublicKeyAlgorithmTags.RSA_SIGN
|
||||
|| pubkey.algorithm == PublicKeyAlgorithmTags.RSA_ENCRYPT
|
||||
|
||||
val callback = object : HardwareSecurity.DecryptionCallback {
|
||||
override fun decryptSessionKey(
|
||||
|
@ -27,25 +50,49 @@ class YubikeyDataDecryptorFactory(
|
|||
sessionKeyData: ByteArray,
|
||||
pkeskVersion: Int
|
||||
): ByteArray {
|
||||
// TODO: Move user pin verification somewhere else
|
||||
openpgpSession.verifyUserPin("asdasd".toCharArray(), true)
|
||||
if(rsa) {
|
||||
val decryptedSessionKey = openpgpSession.decrypt(sessionKeyData)
|
||||
|
||||
if(isRSAKey) {
|
||||
// easy
|
||||
val decryptedSessionKey = openpgpSession.decrypt(sessionKeyData)
|
||||
return decryptedSessionKey
|
||||
} else {
|
||||
/*
|
||||
val secret = openpgpSession.decrypt(sessionKeyData)
|
||||
val hashAlgorithm: Int = ecPubKey.getHashAlgorithm().toInt()
|
||||
val symmetricKeyAlgorithm: Int = ecPubKey.getSymmetricKeyAlgorithm().toInt()
|
||||
// meh...
|
||||
val ecPubKey: ECDHPublicBCPGKey = pubkey.publicKeyPacket.key as ECDHPublicBCPGKey
|
||||
// split session data into peer key and encrypted session key
|
||||
|
||||
// peer key
|
||||
val pLen =
|
||||
((((sessionKeyData[0].toInt() and 0xff) shl 8) + (sessionKeyData[1].toInt() and 0xff)) + 7) / 8
|
||||
checkRange(2 + pLen + 1, sessionKeyData)
|
||||
val pEnc = ByteArray(pLen)
|
||||
System.arraycopy(sessionKeyData, 2, pEnc, 0, pLen)
|
||||
|
||||
// encrypted session key
|
||||
val keyLen = sessionKeyData[pLen + 2].toInt() and 0xff
|
||||
checkRange(2 + pLen + 1 + keyLen, sessionKeyData)
|
||||
val keyEnc = ByteArray(keyLen)
|
||||
System.arraycopy(sessionKeyData, 2 + pLen + 1, keyEnc, 0, keyLen)
|
||||
|
||||
// perform ECDH key agreement via the Yubikey
|
||||
val secret = openpgpSession.decrypt(pEnc)
|
||||
|
||||
// Use the shared key to decrypt the session key
|
||||
val hashAlgorithm: Int = ecPubKey.hashAlgorithm.toInt()
|
||||
val symmetricKeyAlgorithm: Int = ecPubKey.symmetricKeyAlgorithm.toInt()
|
||||
val userKeyingMaterial = RFC6637Utils.createUserKeyingMaterial(
|
||||
this.pgpPrivKey.getPublicKeyPacket(),
|
||||
pubkey.publicKeyPacket,
|
||||
BcKeyFingerprintCalculator(),
|
||||
)
|
||||
val rfc6637KDFCalculator = RFC6637KDFCalculator(
|
||||
BcPGPDigestCalculatorProvider()[hashAlgorithm], symmetricKeyAlgorithm,
|
||||
val rfc6637KDFCalculator =
|
||||
RFC6637KDFCalculator(
|
||||
BcPGPDigestCalculatorProvider()[hashAlgorithm],
|
||||
symmetricKeyAlgorithm,
|
||||
)
|
||||
val key =
|
||||
KeyParameter(rfc6637KDFCalculator.createKey(secret, userKeyingMaterial))
|
||||
|
||||
return PGPPad.unpadSessionData(
|
||||
BcPublicKeyDataDecryptorFactory.unwrapSessionData(
|
||||
keyEnc,
|
||||
|
@ -53,14 +100,11 @@ class YubikeyDataDecryptorFactory(
|
|||
key,
|
||||
),
|
||||
)
|
||||
*/
|
||||
throw UnsupportedOperationException("ECDH decryption is not yet implemented.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return YubikeyDataDecryptorFactory(smartCardConnection, callback, decKeyIdentifier)
|
||||
return YubikeyDataDecryptorFactory(callback, decKeyIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue