Skip to content
Open
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 @@ -35,5 +35,70 @@ public void ToSignatureAlgorithm_InvalidKeySpecs_ThrowsArgumentException(KeyType
// Act & Assert
Assert.Throws<ArgumentException>(() => keySpec.ToKeyVaultSignatureAlgorithm());
}

[Theory]
[InlineData(KeyType.RSA, 2048, "RS256")]
[InlineData(KeyType.RSA, 3072, "RS384")]
[InlineData(KeyType.RSA, 4096, "RS512")]
[InlineData(KeyType.EC, 256, "ES256")]
[InlineData(KeyType.EC, 384, "ES384")]
[InlineData(KeyType.EC, 521, "ES512")]
public void ToKeyVaultSignatureAlgorithmPKCS1_ValidKeySpecs_ReturnsCorrectAlgorithm(KeyType keyType, int keySize, string expectedAlgorithm)
{
// Arrange
var keySpec = new KeySpec(keyType, keySize);

// Act
var signatureAlgorithm = keySpec.ToKeyVaultSignatureAlgorithmPKCS1();

// Assert
Assert.Equal(expectedAlgorithm, signatureAlgorithm);
}

[Theory]
[InlineData(KeyType.RSA, 1024)]
[InlineData(KeyType.EC, 128)]
public void ToKeyVaultSignatureAlgorithmPKCS1_InvalidKeySpecs_ThrowsArgumentException(KeyType keyType, int keySize)
{
// Arrange
var keySpec = new KeySpec(keyType, keySize);

// Act & Assert
Assert.Throws<ArgumentException>(() => keySpec.ToKeyVaultSignatureAlgorithmPKCS1());
}

[Theory]
[InlineData(KeyType.RSA, 2048, null, "PS256")] // Default: PSS
[InlineData(KeyType.RSA, 2048, "", "PS256")] // Empty: PSS
[InlineData(KeyType.RSA, 2048, "rsassa-pss", "PS256")] // Explicit PSS
[InlineData(KeyType.RSA, 2048, "rsassa-pkcs1-v1_5", "RS256")] // PKCS1 routing
[InlineData(KeyType.RSA, 3072, "rsassa-pkcs1-v1_5", "RS384")] // PKCS1 3072
[InlineData(KeyType.RSA, 4096, "rsassa-pkcs1-v1_5", "RS512")] // PKCS1 4096
[InlineData(KeyType.EC, 256, "rsassa-pkcs1-v1_5", "ES256")] // EC unaffected
[InlineData(KeyType.EC, 384, "rsassa-pkcs1-v1_5", "ES384")] // EC 384
[InlineData(KeyType.EC, 521, "rsassa-pkcs1-v1_5", "ES512")] // EC 521
public void ToSignatureAlgorithm_WithScheme_ReturnsCorrectAlgorithm(KeyType keyType, int keySize, string? scheme, string expectedAlgorithm)
{
// Arrange
var keySpec = new KeySpec(keyType, keySize);

// Act
var signatureAlgorithm = keySpec.ToKeyVaultSignatureAlgorithm(scheme);

// Assert
Assert.Equal(expectedAlgorithm, signatureAlgorithm);
}

[Fact]
public void ToSignatureAlgorithm_WithInvalidScheme_ThrowsArgumentException()
{
// Arrange
var keySpec = new KeySpec(KeyType.RSA, 2048);

// Act & Assert
var ex = Assert.Throws<ArgumentException>(() => keySpec.ToKeyVaultSignatureAlgorithm("invalid-scheme"));
Assert.Contains("rsassa-pss", ex.Message);
Assert.Contains("rsassa-pkcs1-v1_5", ex.Message);
}
}
}
}
65 changes: 65 additions & 0 deletions Notation.Plugin.AzureKeyVault.Tests/Protocol/KeySpecTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,70 @@ public void KeySpec_EncodeKeySpecAndToSigningAlgorithm_ThrowsArgumentExceptionFo
Assert.Throws<ArgumentException>(() => keySpec.EncodeKeySpec());
Assert.Throws<ArgumentException>(() => keySpec.ToSigningAlgorithm());
}

[Theory]
[InlineData(KeyType.RSA, 2048, "RSASSA-PKCS1-v1_5-SHA-256")]
[InlineData(KeyType.RSA, 3072, "RSASSA-PKCS1-v1_5-SHA-384")]
[InlineData(KeyType.RSA, 4096, "RSASSA-PKCS1-v1_5-SHA-512")]
[InlineData(KeyType.EC, 256, "ECDSA-SHA-256")]
[InlineData(KeyType.EC, 384, "ECDSA-SHA-384")]
[InlineData(KeyType.EC, 521, "ECDSA-SHA-512")]
public void KeySpec_ToSigningAlgorithmPKCS1_ReturnsCorrectValues(KeyType keyType, int size, string expectedSigningAlgorithm)
{
// Arrange
KeySpec keySpec = new KeySpec(keyType, size);

// Act
string signingAlgorithm = keySpec.ToSigningAlgorithmPKCS1();

// Assert
Assert.Equal(expectedSigningAlgorithm, signingAlgorithm);
}

[Theory]
[InlineData(KeyType.RSA, 1024)]
[InlineData(KeyType.EC, 128)]
public void KeySpec_ToSigningAlgorithmPKCS1_ThrowsArgumentExceptionForInvalidSizes(KeyType keyType, int size)
{
// Arrange
KeySpec keySpec = new KeySpec(keyType, size);

// Act & Assert
Assert.Throws<ArgumentException>(() => keySpec.ToSigningAlgorithmPKCS1());
}

[Theory]
[InlineData(KeyType.RSA, 2048, null, "RSASSA-PSS-SHA-256")] // Default: PSS
[InlineData(KeyType.RSA, 2048, "", "RSASSA-PSS-SHA-256")] // Empty: PSS
[InlineData(KeyType.RSA, 2048, "rsassa-pss", "RSASSA-PSS-SHA-256")] // Explicit PSS
[InlineData(KeyType.RSA, 2048, "rsassa-pkcs1-v1_5", "RSASSA-PKCS1-v1_5-SHA-256")] // PKCS1 routing
[InlineData(KeyType.RSA, 3072, "rsassa-pkcs1-v1_5", "RSASSA-PKCS1-v1_5-SHA-384")] // PKCS1 3072
[InlineData(KeyType.RSA, 4096, "rsassa-pkcs1-v1_5", "RSASSA-PKCS1-v1_5-SHA-512")] // PKCS1 4096
[InlineData(KeyType.EC, 256, "rsassa-pkcs1-v1_5", "ECDSA-SHA-256")] // EC unaffected
[InlineData(KeyType.EC, 384, "rsassa-pkcs1-v1_5", "ECDSA-SHA-384")] // EC 384
[InlineData(KeyType.EC, 521, "rsassa-pkcs1-v1_5", "ECDSA-SHA-512")] // EC 521
public void KeySpec_ToSigningAlgorithmWithScheme_ReturnsCorrectValues(KeyType keyType, int size, string? scheme, string expectedSigningAlgorithm)
{
// Arrange
KeySpec keySpec = new KeySpec(keyType, size);

// Act
string signingAlgorithm = keySpec.ToSigningAlgorithm(scheme);

// Assert
Assert.Equal(expectedSigningAlgorithm, signingAlgorithm);
}

[Fact]
public void KeySpec_ToSigningAlgorithmWithInvalidScheme_ThrowsArgumentException()
{
// Arrange
KeySpec keySpec = new KeySpec(KeyType.RSA, 2048);

// Act & Assert
var ex = Assert.Throws<ArgumentException>(() => keySpec.ToSigningAlgorithm("invalid-scheme"));
Assert.Contains("rsassa-pss", ex.Message);
Assert.Contains("rsassa-pkcs1-v1_5", ex.Message);
}
}
}
24 changes: 21 additions & 3 deletions Notation.Plugin.AzureKeyVault/Command/GenerateSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ namespace Notation.Plugin.AzureKeyVault.Command
/// </summary>
public class GenerateSignature : IPluginCommand
{
/// <summary>
/// Plugin config key for specifying the signing scheme.
/// Supported values: "rsassa-pss" (default), "rsassa-pkcs1-v1_5"
/// </summary>
public const string SigningSchemeConfigKey = "signing_scheme";

private GenerateSignatureRequest _request;
private IKeyVaultClient _keyVaultClient;

Expand Down Expand Up @@ -104,13 +110,25 @@ public async Task<IPluginResponse> RunAsync()
// Extract KeySpec from the certificate
var keySpec = leafCert.KeySpec();

// Sign
var signature = await _keyVaultClient.SignAsync(keySpec.ToKeyVaultSignatureAlgorithm(), _request.Payload);
// Get the signing scheme from plugin config (default: rsassa-pss)
// Supported values:
// - "rsassa-pss" (default): RSASSA-PSS padding for JWS/COSE signatures
// - "rsassa-pkcs1-v1_5": RSASSA-PKCS1-v1_5 padding for PKCS#7/dm-verity signatures
string? signingScheme = _request.PluginConfig?.GetValueOrDefault(SigningSchemeConfigKey);

// Determine the Azure Key Vault signature algorithm based on scheme
var akvAlgorithm = keySpec.ToKeyVaultSignatureAlgorithm(signingScheme);

// Sign using the selected algorithm
var signature = await _keyVaultClient.SignAsync(akvAlgorithm, _request.Payload);

// Determine the notation signing algorithm string based on scheme
var signingAlgorithm = keySpec.ToSigningAlgorithm(signingScheme);

return new GenerateSignatureResponse(
keyId: _request.KeyId,
signature: signature,
signingAlgorithm: keySpec.ToSigningAlgorithm(),
signingAlgorithm: signingAlgorithm,
certificateChain: certChain.Select(x => x.RawData).ToList());
}
}
Expand Down
44 changes: 42 additions & 2 deletions Notation.Plugin.AzureKeyVault/KeyVault/KeySpecExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace Notation.Plugin.AzureKeyVault.Client
/// </summary>
public static class KeySpecExtension
{

/// <summary>
/// Get SignatureAlgorithm from KeySpec for Azure Key Vault signing.
/// Uses RSASSA-PSS (PS256/PS384/PS512) for RSA keys (default for JWS/COSE).
/// </summary>
public static SignatureAlgorithm ToKeyVaultSignatureAlgorithm(this KeySpec keySpec) => keySpec.Type switch
{
Expand All @@ -30,5 +30,45 @@ public static class KeySpecExtension
},
_ => throw new ArgumentException($"Invalid KeySpec with type {keySpec.Type}")
};

/// <summary>
/// Get SignatureAlgorithm from KeySpec for Azure Key Vault signing using RSASSA-PKCS1-v1_5.
/// Uses RS256/RS384/RS512 for RSA keys (required for PKCS#7/dm-verity).
/// For EC keys, returns the standard ECDSA algorithm (no padding change).
/// </summary>
public static SignatureAlgorithm ToKeyVaultSignatureAlgorithmPKCS1(this KeySpec keySpec) => keySpec.Type switch
{
KeyType.RSA => keySpec.Size switch
{
2048 => SignatureAlgorithm.RS256,
3072 => SignatureAlgorithm.RS384,
4096 => SignatureAlgorithm.RS512,
_ => throw new ArgumentException($"Invalid KeySpec for RSA with size {keySpec.Size}")
},
KeyType.EC => keySpec.Size switch
{
// ECDSA doesn't have padding variants - use the same algorithm
256 => SignatureAlgorithm.ES256,
384 => SignatureAlgorithm.ES384,
521 => SignatureAlgorithm.ES512,
_ => throw new ArgumentException($"Invalid KeySpec for EC with size {keySpec.Size}")
},
_ => throw new ArgumentException($"Invalid KeySpec with type {keySpec.Type}")
};

/// <summary>
/// Get SignatureAlgorithm from KeySpec for Azure Key Vault signing based on the specified signing scheme.
/// </summary>
/// <param name="keySpec">The key specification</param>
/// <param name="scheme">The signing scheme (rsassa-pss or rsassa-pkcs1-v1_5). Default is rsassa-pss.</param>
/// <returns>The Azure Key Vault SignatureAlgorithm</returns>
public static SignatureAlgorithm ToKeyVaultSignatureAlgorithm(this KeySpec keySpec, string? scheme) => scheme?.ToLowerInvariant() switch
{
SigningScheme.RSASSA_PKCS1_V1_5 => keySpec.ToKeyVaultSignatureAlgorithmPKCS1(),
SigningScheme.RSASSA_PSS => keySpec.ToKeyVaultSignatureAlgorithm(),
null => keySpec.ToKeyVaultSignatureAlgorithm(),
"" => keySpec.ToKeyVaultSignatureAlgorithm(),
_ => throw new ArgumentException($"Invalid signing scheme: {scheme}. Supported values are '{SigningScheme.RSASSA_PSS}' and '{SigningScheme.RSASSA_PKCS1_V1_5}'")
};
}
}
}
67 changes: 66 additions & 1 deletion Notation.Plugin.AzureKeyVault/Protocol/KeySpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,40 @@ public static class KeySpecConstants
/// </summary>
public static class SigningAlgorithms
{
// RSASSA-PSS (default for JWS/COSE)
public const string RSASSA_PSS_SHA_256 = "RSASSA-PSS-SHA-256";
public const string RSASSA_PSS_SHA_384 = "RSASSA-PSS-SHA-384";
public const string RSASSA_PSS_SHA_512 = "RSASSA-PSS-SHA-512";

// RSASSA-PKCS1-v1_5 (required for PKCS#7/dm-verity)
public const string RSASSA_PKCS1_V1_5_SHA_256 = "RSASSA-PKCS1-v1_5-SHA-256";
public const string RSASSA_PKCS1_V1_5_SHA_384 = "RSASSA-PKCS1-v1_5-SHA-384";
public const string RSASSA_PKCS1_V1_5_SHA_512 = "RSASSA-PKCS1-v1_5-SHA-512";

// ECDSA
public const string ECDSA_SHA_256 = "ECDSA-SHA-256";
public const string ECDSA_SHA_384 = "ECDSA-SHA-384";
public const string ECDSA_SHA_512 = "ECDSA-SHA-512";
}

/// <summary>
/// Defines the supported signing schemes (padding modes for RSA).
/// </summary>
public static class SigningScheme
{
/// <summary>
/// RSASSA-PSS padding (default for JWS/COSE notation signatures).
/// </summary>
public const string RSASSA_PSS = "rsassa-pss";

/// <summary>
/// RSASSA-PKCS1-v1_5 padding (required for PKCS#7/dm-verity signatures).
/// This scheme is needed for Linux kernel dm-verity verification which
/// only supports PKCS#1 v1.5 signatures.
/// </summary>
public const string RSASSA_PKCS1_V1_5 = "rsassa-pkcs1-v1_5";
}

/// <summary>
/// KeyType class.
/// </summary>
Expand Down Expand Up @@ -87,7 +113,7 @@ public KeySpec(KeyType type, int size)
};

/// <summary>
/// Convert KeySpec to be SigningAlgorithm string.
/// Convert KeySpec to be SigningAlgorithm string (RSASSA-PSS for RSA, default).
/// Supported key types are RSA with key size 2048, 3072, 4096
/// and ECDSA with key size 256, 384, 521.
/// </summary>
Expand All @@ -109,5 +135,44 @@ public KeySpec(KeyType type, int size)
},
_ => throw new ArgumentException($"Invalid KeySpec Type: {Type}")
};

/// <summary>
/// Convert KeySpec to be SigningAlgorithm string using RSASSA-PKCS1-v1_5.
/// This is required for PKCS#7 signatures used in dm-verity.
/// For EC keys, this returns the standard ECDSA algorithm (no padding change).
/// </summary>
public string ToSigningAlgorithmPKCS1() => Type switch
{
KeyType.RSA => Size switch
{
2048 => SigningAlgorithms.RSASSA_PKCS1_V1_5_SHA_256,
3072 => SigningAlgorithms.RSASSA_PKCS1_V1_5_SHA_384,
4096 => SigningAlgorithms.RSASSA_PKCS1_V1_5_SHA_512,
_ => throw new ArgumentException($"Invalid RSA KeySpec size {Size}")
},
KeyType.EC => Size switch
{
// ECDSA doesn't have padding variants - use the same algorithm
256 => SigningAlgorithms.ECDSA_SHA_256,
384 => SigningAlgorithms.ECDSA_SHA_384,
521 => SigningAlgorithms.ECDSA_SHA_512,
_ => throw new ArgumentException($"Invalid EC KeySpec size {Size}")
},
_ => throw new ArgumentException($"Invalid KeySpec Type: {Type}")
};

/// <summary>
/// Convert KeySpec to SigningAlgorithm string based on the specified signing scheme.
/// </summary>
/// <param name="scheme">The signing scheme to use (rsassa-pss or rsassa-pkcs1-v1_5)</param>
/// <returns>The appropriate signing algorithm string</returns>
public string ToSigningAlgorithm(string? scheme) => scheme?.ToLowerInvariant() switch
{
SigningScheme.RSASSA_PKCS1_V1_5 => ToSigningAlgorithmPKCS1(),
SigningScheme.RSASSA_PSS => ToSigningAlgorithm(),
null => ToSigningAlgorithm(),
"" => ToSigningAlgorithm(),
_ => throw new ArgumentException($"Invalid signing scheme: {scheme}. Supported values are '{SigningScheme.RSASSA_PSS}' and '{SigningScheme.RSASSA_PKCS1_V1_5}'")
};
}
}
Loading