Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fix] Removed TDES as default assumption for PivSession.PinOnly #167

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using Yubico.Core.Iso7816;

namespace Yubico.YubiKey.Piv.Commands
Expand Down Expand Up @@ -306,18 +307,25 @@ public sealed class InitializeAuthenticateManagementKeyCommand : IYubiKeyCommand
/// </value>
public YubiKeyApplication Application => YubiKeyApplication.Piv;


[Obsolete("This constructor is deprecated. Users must specify management key algorithm type, as it cannot be assumed.")]
public InitializeAuthenticateManagementKeyCommand()
: this(true)
{
}

/// <summary>
/// Initializes a new instance of the InitializeAuthenticateManagementKeyCommand class for
/// Mutual Authentication, and a Triple-DES management key.
/// Mutual Authentication.
/// </summary>
/// <remarks>
/// Using this constructor is equivalent to
/// <code language="csharp">
/// new InitializeAuthenticateManagementKeyCommand(true);
/// new InitializeAuthenticateManagementKeyCommand(true, PivAlgorithm.AES192);
/// </code>
/// </remarks>
public InitializeAuthenticateManagementKeyCommand()
: this(true)
public InitializeAuthenticateManagementKeyCommand(PivAlgorithm algorithm)
: this(true, algorithm)
{
}

Expand All @@ -335,6 +343,7 @@ public InitializeAuthenticateManagementKeyCommand()
/// <param name="mutualAuthentication">
/// <c>True</c> for mutual authentication, <c>false</c> for single.
/// </param>
[Obsolete("This constructor is deprecated. Users must specify management key algorithm type, as it cannot be assumed.")]
public InitializeAuthenticateManagementKeyCommand(bool mutualAuthentication)
: this(mutualAuthentication, PivAlgorithm.TripleDes)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,7 @@ public sealed class InitializeAuthenticateManagementKeyResponse : PivResponse, I
// ResponseApdu.Data. It will be 8 bytes.
private readonly byte[]? _clientAuthenticationChallenge;

/// <summary>
/// Constructs an InitializeAuthenticateManagementKeyResponse based on a ResponseApdu
/// received from the YubiKey for the Triple-DES algorithm.
/// </summary>
/// <param name="responseApdu">
/// The object containing the Response APDU<br/>returned by the YubiKey.
/// </param>
/// <exception cref="MalformedYubiKeyResponseException">
/// Thrown when the data provided does not meet the expectations, and cannot be parsed.
/// </exception>
[Obsolete("This constructor is deprecated. Users must specify management key algorithm type, as it cannot be assumed.")]
public InitializeAuthenticateManagementKeyResponse(ResponseApdu responseApdu)
: this(responseApdu, PivAlgorithm.TripleDes)
{
Expand All @@ -90,8 +81,8 @@ public InitializeAuthenticateManagementKeyResponse(ResponseApdu responseApdu)
/// <exception cref="MalformedYubiKeyResponseException">
/// Thrown when the data provided does not meet the expectations, and cannot be parsed.
/// </exception>
public InitializeAuthenticateManagementKeyResponse(ResponseApdu responseApdu, PivAlgorithm algorithm) :
base(responseApdu)
public InitializeAuthenticateManagementKeyResponse(ResponseApdu responseApdu, PivAlgorithm algorithm)
: base(responseApdu)
{
Algorithm = algorithm;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ private SetManagementKeyCommand()
throw new NotImplementedException();
}

[Obsolete("This constructor is deprecated. Users must specify management key algorithm type, as it cannot be assumed.")]
public SetManagementKeyCommand(ReadOnlyMemory<byte> newKey)
: this(newKey, PivTouchPolicy.Default, PivAlgorithm.TripleDes)
{
}

/// <summary>
/// Initializes a new instance of the <c>SetManagementKeyCommand</c> class.
/// This command takes the new management key as input and will set the
Expand All @@ -176,11 +182,9 @@ private SetManagementKeyCommand()
/// };
/// </code>
/// <para>
/// The default algorithm is <c>TripleDes</c>. If you do not set the
/// <c>Algorithm</c> property after instantiating with this constructor,
/// the SDK will expect the key to be TripleDES. Valid algorithms are
/// <c>PivAlgorithm.TripleDes</c>, <c>PivAlgorithm.Aes128</c>,
/// <c>PivAlgorithm.Aes192</c>, and <c>PivAlgorithm.Aes256</c>.
/// Valid algorithms are <c>PivAlgorithm.TripleDes</c>,
/// <c>PivAlgorithm.Aes128</c>, <c>PivAlgorithm.Aes192</c>, and
/// <c>PivAlgorithm.Aes256</c>. FIPS YubiKeys versions 5.7 and greater require <c>PivAlgorithm.Aes192</c>.
/// </para>
/// <para>
/// Note that you need to authenticate the current PIV management key before
Expand All @@ -190,36 +194,15 @@ private SetManagementKeyCommand()
/// <param name="newKey">
/// The bytes that make up the new management key.
/// </param>
public SetManagementKeyCommand(ReadOnlyMemory<byte> newKey)
: this(newKey, PivTouchPolicy.Default, PivAlgorithm.TripleDes)
/// <param name="algorithm">
/// The algorithm of the new management key.
/// </param>
public SetManagementKeyCommand(ReadOnlyMemory<byte> newKey, PivAlgorithm algorithm)
: this(newKey, PivTouchPolicy.Default, algorithm)
{
}

/// <summary>
/// Initializes a new instance of the SetManagementKeyCommand class. This
/// command takes the new management key and the touch policy as input.
/// </summary>
/// <remarks>
/// Note that a <c>touchPolicy</c> of <c>PivTouchPolicy.Default</c> or
/// <c>None</c> is equivalent to <c>Never</c>.
/// <para>
/// The default algorithm is <c>TripleDes</c>. If you do not set the
/// <c>Algorithm</c> property after instantiating with this constructor,
/// the SDK will expect the key to be TripleDES. Valid algorithms are
/// <c>PivAlgorithm.TripleDes</c>, <c>PivAlgorithm.Aes128</c>,
/// <c>PivAlgorithm.Aes192</c>, and <c>PivAlgorithm.Aes256</c>.
/// </para>
/// <para>
/// Note also that you need to authenticate the current PIV management
/// key before setting it to a new value with this command.
/// </para>
/// </remarks>
/// <param name="newKey">
/// The bytes that make up the new management key.
/// </param>
/// <param name="touchPolicy">
/// The touch policy for the management key.
/// </param>
[Obsolete("This constructor is deprecated. Users must specify management key algorithm type, as it cannot be assumed.")]
public SetManagementKeyCommand(ReadOnlyMemory<byte> newKey, PivTouchPolicy touchPolicy)
: this(newKey, touchPolicy, PivAlgorithm.TripleDes)
{
Expand All @@ -236,7 +219,7 @@ public SetManagementKeyCommand(ReadOnlyMemory<byte> newKey, PivTouchPolicy touch
/// <para>
/// Valid algorithms are <c>PivAlgorithm.TripleDes</c>,
/// <c>PivAlgorithm.Aes128</c>, <c>PivAlgorithm.Aes192</c>, and
/// <c>PivAlgorithm.Aes256</c>,
/// <c>PivAlgorithm.Aes256</c>. FIPS YubiKeys versions 5.7 and greater require <c>PivAlgorithm.Aes192</c>.
/// </para>
/// <para>
/// Note also that you need to authenticate the current PIV management
Expand Down
59 changes: 29 additions & 30 deletions Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.Pinonly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ public PivPinOnlyMode TryRecoverPinOnlyMode()
// If we can authenticate the mgmt key, then set ADMIN DATA and
// PRINTED.
var userKeyCollector = KeyCollector;
using var specialKeyCollector = new SpecialKeyCollector();
using var specialKeyCollector = new SpecialKeyCollector(DefaultManagementKeyAlgorithm);

try
{
Expand Down Expand Up @@ -349,7 +349,7 @@ private PivPinOnlyMode TryAuthenticatePinOnly(bool trustAdminData)
}

var userKeyCollector = KeyCollector;
using var specialKeyCollector = new SpecialKeyCollector();
using var specialKeyCollector = new SpecialKeyCollector(DefaultManagementKeyAlgorithm);

try
{
Expand Down Expand Up @@ -513,7 +513,7 @@ private PivPinOnlyMode GetPinDerivedStatus(

/// <summary>
/// Set the YubiKey's PIV application to be PIN-only with a PIN-derived
/// and/or PIN-Protected Triple-DES management key. This sets the
/// and/or PIN-Protected management key (Firmware 5.7.x and later: AES-192. Firmware 5.6.x and earlier: TDES.). This sets the
/// YubiKey to either
/// <code>
/// PivPinOnlyMode.PinProtected
Expand Down Expand Up @@ -544,7 +544,7 @@ private PivPinOnlyMode GetPinDerivedStatus(
/// </para>
/// <para>
/// Note also that this will make sure that the management key algorithm
/// will be Triple-DES, even if the current management key is a different
/// will be default management key algorithm (Firmware 5.7.x and later: AES-192. Firmware 5.6.x and earlier: TDES.), even if the current management key is a different
/// algorithm. This behavior matches how this method operated in previous
/// versions of the SDK.
/// </para>
Expand All @@ -554,7 +554,7 @@ private PivPinOnlyMode GetPinDerivedStatus(
/// </param>
/// <exception cref="InvalidOperationException">
/// There is no <c>KeyCollector</c> loaded, one of the keys provided was
/// not a valid Triple-DES key, the data stored on the YubiKey is
/// not of a valid key algorithm type (Firmware 5.7.x and later: AES-192. Firmware 5.6.x and earlier: TDES.), the data stored on the YubiKey is
/// incompatible with PIN-only, or the YubiKey had some other error, such
/// as unreliable connection.
/// </exception>
Expand All @@ -566,7 +566,7 @@ private PivPinOnlyMode GetPinDerivedStatus(
/// authenticated, or the remaining retries count indicates the PIN is
/// blocked.
/// </exception>
public void SetPinOnlyMode(PivPinOnlyMode pinOnlyMode) => SetPinOnlyMode(pinOnlyMode, PivAlgorithm.TripleDes);
public void SetPinOnlyMode(PivPinOnlyMode pinOnlyMode) => SetPinOnlyMode(pinOnlyMode, DefaultManagementKeyAlgorithm);

/// <summary>
/// Set the YubiKey's PIV application to be PIN-only with a PIN-derived
Expand Down Expand Up @@ -604,13 +604,13 @@ private PivPinOnlyMode GetPinDerivedStatus(
/// The management key derived and/or stored in PRINTED will be for the
/// specified algorithm. For all YubiKeys, <c>TripleDes</c> is a valid
/// algorithm. For YubiKeys 5.4.2 and later, it is possible to set the
/// management key to an AES key. Before setting the
/// <c>mgmtKeyAlgorithm</c> arg to an AES algorithm, make sure it is
/// management key to an AES key. For YubiKeys 5.7 and later, AES192 is the default.
/// Before setting the <c>mgmtKeyAlgorithm</c> arg to an AES algorithm, make sure it is
/// allowed on the YubiKey. You can use the <c>HasFeature</c> call. For
/// example,
/// <code language="csharp">
/// PivAlgorithm mgmtKeyAlgorithm = yubiKey.HasFeature(YubiKeyFeature.PivAesManagementKey) ?
/// PivAlgorithm.Aes128 : PivAlgorithm.TripleDes;
/// PivAlgorithm.Aes192 : PivAlgorithm.TripleDes;
/// pivSession.SetPinOnlyMode(PivPinOnlyMode.PinProtected, mgmtKeyAlgorithm);
/// </code>
/// If the algorithm is not supported by the YubiKey, this method will
Expand Down Expand Up @@ -641,16 +641,11 @@ private PivPinOnlyMode GetPinDerivedStatus(
/// currently set to PIN-only (and neither PinProtected nor PinDerived is
/// Unavailable), this method will remove the contents of the storage
/// locations ADMIN DATA and PRINTED, and reset the management key to the
/// default:
/// <code>
/// Triple-DES
/// 0x01 02 03 04 05 06 07 08
/// 01 02 03 04 05 06 07 08
/// 01 02 03 04 05 06 07 08
/// </code>
/// default management key.
/// In this case, the <c>mgmtKeyAlgorithm</c> arg will be ignored, the
/// management key's algorithm after removing PIN-only status will be
/// Triple-DES. The touch policy of the management key will also be set
/// the default management key algorithm (Firmware 5.7.x and later: AES-192. Firmware 5.6.x and earlier: TDES.).
/// The touch policy of the management key will also be set
/// to the default (Never). Note that the management key must be
/// authenticated and the PIN verified in order to perform this task.
/// This method will authenticate the management key using the PIN-only
Expand Down Expand Up @@ -732,7 +727,7 @@ private PivPinOnlyMode GetPinDerivedStatus(
/// </param>
/// <exception cref="InvalidOperationException">
/// There is no <c>KeyCollector</c> loaded, one of the keys provided was
/// not a valid Triple-DES key, the data stored on the YubiKey is
/// not of a valid key algorithm type (Firmware 5.7.x and later: AES-192. Firmware 5.6.x and earlier: TDES.), the data stored on the YubiKey is
/// incompatible with PIN-only, or the YubiKey had some other error, such
/// as unreliable connection.
/// </exception>
Expand All @@ -751,7 +746,7 @@ public void SetPinOnlyMode(PivPinOnlyMode pinOnlyMode, PivAlgorithm mgmtKeyAlgor
pinOnlyMode.ToString(), mgmtKeyAlgorithm.ToString());

var userKeyCollector = KeyCollector;
using var specialKeyCollector = new SpecialKeyCollector();
using var specialKeyCollector = new SpecialKeyCollector(DefaultManagementKeyAlgorithm);

try
{
Expand All @@ -777,7 +772,7 @@ private void SetPinOnlyMode(ReadOnlyMemory<byte> pin, PivPinOnlyMode pinOnlyMode
}

var userKeyCollector = KeyCollector;
using var specialKeyCollector = new SpecialKeyCollector();
using var specialKeyCollector = new SpecialKeyCollector(DefaultManagementKeyAlgorithm);

try
{
Expand Down Expand Up @@ -835,7 +830,7 @@ private void SetPinOnlyMode(
// Or some other reason.
var newPinOnlyMode = PivPinOnlyMode.None;
var currentPinOnlyMode = GetPrintedPinProtectedStatus(specialKeyCollector, userKeyCollector);

var pinOnlyCheck = CheckPinOnlyStatus(
currentPinOnlyMode, pinOnlyMode, PivPinOnlyMode.PinProtected, PivPinOnlyMode.PinProtectedUnavailable,
newAlgorithm, ref newPinOnlyMode);
Expand Down Expand Up @@ -1028,11 +1023,12 @@ private void ClearPinOnly(PivPinOnlyMode currentMode, SpecialKeyCollector specia
PutEmptyData(AdminDataDataTag);
}

var managementKeyAlgorithm = DefaultManagementKeyAlgorithm;
specialKeyCollector.SetKeyData(
SpecialKeyCollector.SetKeyDataDefault, ReadOnlyMemory<byte>.Empty, isNewKey: true,
PivAlgorithm.TripleDes);
managementKeyAlgorithm);

specialKeyCollector.ChangeManagementKey(this, PivAlgorithm.TripleDes);
specialKeyCollector.ChangeManagementKey(this, managementKeyAlgorithm);
}

private void PutEmptyData(int dataTag)
Expand Down Expand Up @@ -1087,7 +1083,7 @@ private void SetYubiKeyPinDerived(
// because this method will update the current key with the new key.
specialKeyCollector.ChangeManagementKey(this, mgmtKeyAlgorithm);
_ = BlockPinOrPuk(PivSlot.Puk);

adminData.SetSalt(saltBytes);
adminData.PukBlocked = true;
}
Expand Down Expand Up @@ -1150,7 +1146,7 @@ private bool TryGetChangePinMode(ReadOnlyMemory<byte> pin, out PivPinOnlyMode mo
mode = PivPinOnlyMode.None;

var userKeyCollectorFunc = KeyCollector;
using var specialKeyCollector = new SpecialKeyCollector();
using var specialKeyCollector = new SpecialKeyCollector(DefaultManagementKeyAlgorithm);

bool isValid = TryReadObject(out AdminData adminData);

Expand Down Expand Up @@ -1179,17 +1175,18 @@ private bool TryGetChangePinMode(ReadOnlyMemory<byte> pin, out PivPinOnlyMode mo

_ = specialKeyCollector.DeriveKeyData(salt, ManagementKeyAlgorithm, isNewKey: false);

var managementKeyAlgorithm = DefaultManagementKeyAlgorithm;
specialKeyCollector.SetKeyData(
SpecialKeyCollector.SetKeyDataDefault, ReadOnlyMemory<byte>.Empty, isNewKey: true,
PivAlgorithm.TripleDes);
managementKeyAlgorithm);

// If this fails, then the mgmt key is not PIN-derived from the
// PIN and salt, so we'll say it is not PIN-derived.
if (!TryForcedChangeManagementKey(
specialKeyCollector.GetCurrentMgmtKey(),
specialKeyCollector.GetNewMgmtKey(),
PivTouchPolicy.Never,
PivAlgorithm.TripleDes))
managementKeyAlgorithm))
{
return true;
}
Expand Down Expand Up @@ -1405,13 +1402,14 @@ private sealed class SpecialKeyCollector : IDisposable
private readonly MgmtKeyHolder _currentKey;
private readonly Memory<byte> _defaultKey;
private readonly MgmtKeyHolder _newKey;
private readonly PivAlgorithm _defaultManagementKeyAlgorithm;
private readonly byte[] _pinData = new byte[MaxPinLength];
private readonly Memory<byte> _pinMemory;

private bool _disposed;
private int _pinLength;

public SpecialKeyCollector()
public SpecialKeyCollector(PivAlgorithm defaultManagemenyKeyAlgorithm)
{
_defaultKey = new Memory<byte>(
new byte[]
Expand All @@ -1425,7 +1423,8 @@ public SpecialKeyCollector()
_newKey = new MgmtKeyHolder();

// Make sure the current key is init to the default.
_currentKey.SetKeyData(_defaultKey, PivAlgorithm.TripleDes);
_defaultManagementKeyAlgorithm = defaultManagemenyKeyAlgorithm;
_currentKey.SetKeyData(_defaultKey, _defaultManagementKeyAlgorithm);

PinCollected = false;
_pinMemory = new Memory<byte>(_pinData);
Expand Down Expand Up @@ -1494,7 +1493,7 @@ public void SetKeyData(int setFlag, ReadOnlyMemory<byte> keyData, bool isNewKey,
return;
}

destinationKeyHolder.SetKeyData(_defaultKey, PivAlgorithm.TripleDes);
destinationKeyHolder.SetKeyData(_defaultKey, _defaultManagementKeyAlgorithm);
}

// Derive the mgmt key from the PIN in this object, along with the
Expand Down
Loading
Loading