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

[API Proposal]: Additional span overloads for asymmetric signing and encryption #68767

Closed
vcsjones opened this issue May 2, 2022 · 3 comments · Fixed by #73502
Closed

[API Proposal]: Additional span overloads for asymmetric signing and encryption #68767

vcsjones opened this issue May 2, 2022 · 3 comments · Fixed by #73502
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Security
Milestone

Comments

@vcsjones
Copy link
Member

vcsjones commented May 2, 2022

Background and motivation

For asymmetric signing (ECDsa, RSA) we have Sign APIs that accept a byte[] and TrySign APIs that accept a ReadOnlySpan<byte>.

There is not:

  • Sign with an allocating return that accepts a ReadOnlySpan<byte>
  • Sign with a write-to-destination that accepts a ReadOnlySpan<byte> and throws if the destination is too small.
  • For RSA, similarly for encryption.

I propose we fill those API gaps.

API Proposal

namespace System.Security.Cryptography {
    public abstract partial class RSA {
        public byte[] Encrypt(
            ReadOnlySpan<byte> data,
            RSAEncryptionPadding padding);

        public int Encrypt(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            RSAEncryptionPadding padding);

        public byte[] Decrypt(
            ReadOnlySpan<byte> data,
            RSAEncryptionPadding padding);

        public int Decrypt(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            RSAEncryptionPadding padding);

        public byte[] SignData(
            ReadOnlySpan<byte> data,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);

        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);

        public byte[] SignHash(
            ReadOnlySpan<byte> hash,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);

        public int SignHash(
            ReadOnlySpan<byte> hash,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);
    }

    public abstract partial class ECDsa {
        public byte[] SignData(
            ReadOnlySpan<byte> data,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat);

        public byte[] SignData(
            ReadOnlySpan<byte> data,
            HashAlgorithmName hashAlgorithm);
            
        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat);

        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm);

        public byte[] SignHash(
            ReadOnlySpan<byte> hash,
            DSASignatureFormat signatureFormat);

        public byte[] SignHash(
            ReadOnlySpan<byte> hash);
            
        public int SignHash(
            ReadOnlySpan<byte> hash,
            Span<byte> destination,
            DSASignatureFormat signatureFormat);

        public int SignHash(
            ReadOnlySpan<byte> hash,
            Span<byte> destination);
    }
}

API Usage

Usage should be fairly straightforward.

Span<byte> hash = stackalloc byte[SHA256.HashSizeInBytes];
// Populate hash

byte[] signature = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);

Alternative Designs

Do nothing. There are "Try*" APIs that can perform the operations on span, so these APIs don't enable new scenarios, but rather make typical scenarios less cumbersome. This also makes the available overloads more consistence with "the typical four".

Risks

No response

@vcsjones vcsjones added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Security labels May 2, 2022
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label May 2, 2022
@ghost
Copy link

ghost commented May 2, 2022

Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

For asymmetric signing (ECDsa, RSA) we have Sign APIs that accept a byte[] and TrySign APIs that accept a ReadOnlySpan<byte>.

There is not:

  • Sign with an allocating return that accepts a ReadOnlySpan<byte>
  • Sign with a write-to-destination that accepts a ReadOnlySpan<byte> and throws if the destination is too small.
  • For RSA, similarly for encryption.

I propose we fill those API gaps.

API Proposal

namespace System.Security.Cryptography {
    public abstract partial class RSA {
        public byte[] Encrypt(
            ReadOnlySpan<byte> data,
            RSAEncryptionPadding padding);

        public int Encrypt(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            RSAEncryptionPadding padding);

        public byte[] Decrypt(
            ReadOnlySpan<byte> data,
            RSAEncryptionPadding padding);

        public int Decrypt(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            RSAEncryptionPadding padding);

        public byte[] SignData(
            ReadOnlySpan<byte> data,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);

        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);

        public byte[] SignHash(
            ReadOnlySpan<byte> hash,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);

        public int SignHash(
            ReadOnlySpan<byte> hash,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding);
    }

    public abstract partial class ECDsa {
        public byte[] SignData(
            ReadOnlySpan<byte> data,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat);

        public byte[] SignData(
            ReadOnlySpan<byte> data,
            HashAlgorithmName hashAlgorithm);
            
        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat);

        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            HashAlgorithmName hashAlgorithm);

        public byte[] SignHash(
            ReadOnlySpan<byte> hash,
            DSASignatureFormat signatureFormat);

        public byte[] SignHash(
            ReadOnlySpan<byte> hash);
            
        public int SignHash(
            ReadOnlySpan<byte> hash,
            Span<byte> destination,
            DSASignatureFormat signatureFormat);

        public int SignHash(
            ReadOnlySpan<byte> hash,
            Span<byte> destination);
    }
}

API Usage

Usage should be fairly straightforward.

Span<byte> hash = stackalloc byte[SHA256.HashSizeInBytes];
// Populate hash

byte[] signature = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);

Alternative Designs

Do nothing. There are "Try*" APIs that can perform the operations on span, so these APIs don't enable new scenarios, but rather make typical scenarios less cumbersome.

Risks

No response

Author: vcsjones
Assignees: -
Labels:

api-suggestion, area-System.Security

Milestone: -

@bartonjs bartonjs added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels May 2, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label May 2, 2022
@bartonjs bartonjs modified the milestones: Future, 7.0.0 May 2, 2022
@bartonjs
Copy link
Member

bartonjs commented May 2, 2022

Looks good to me. There are a good number of Debug.Fail/unreachable blocks that can be deleted with the int-returning-Span-writing variant.

@terrajobst
Copy link
Member

terrajobst commented Jul 26, 2022

Video

  • Looks good as proposed
namespace System.Security.Cryptography;

public abstract partial class RSA
{
    public byte[] Encrypt(
        ReadOnlySpan<byte> data,
        RSAEncryptionPadding padding);

    public int Encrypt(
        ReadOnlySpan<byte> data,
        Span<byte> destination,
        RSAEncryptionPadding padding);

    public byte[] Decrypt(
        ReadOnlySpan<byte> data,
        RSAEncryptionPadding padding);

    public int Decrypt(
        ReadOnlySpan<byte> data,
        Span<byte> destination,
        RSAEncryptionPadding padding);

    public byte[] SignData(
        ReadOnlySpan<byte> data,
        HashAlgorithmName hashAlgorithm,
        RSASignaturePadding padding);

    public int SignData(
        ReadOnlySpan<byte> data,
        Span<byte> destination,
        HashAlgorithmName hashAlgorithm,
        RSASignaturePadding padding);

    public byte[] SignHash(
        ReadOnlySpan<byte> hash,
        HashAlgorithmName hashAlgorithm,
        RSASignaturePadding padding);

    public int SignHash(
        ReadOnlySpan<byte> hash,
        Span<byte> destination,
        HashAlgorithmName hashAlgorithm,
        RSASignaturePadding padding);
}

public abstract partial class ECDsa
{
    public byte[] SignData(
        ReadOnlySpan<byte> data,
        HashAlgorithmName hashAlgorithm,
        DSASignatureFormat signatureFormat);

    public byte[] SignData(
        ReadOnlySpan<byte> data,
        HashAlgorithmName hashAlgorithm);
        
    public int SignData(
        ReadOnlySpan<byte> data,
        Span<byte> destination,
        HashAlgorithmName hashAlgorithm,
        DSASignatureFormat signatureFormat);

    public int SignData(
        ReadOnlySpan<byte> data,
        Span<byte> destination,
        HashAlgorithmName hashAlgorithm);

    public byte[] SignHash(
        ReadOnlySpan<byte> hash,
        DSASignatureFormat signatureFormat);

    public byte[] SignHash(
        ReadOnlySpan<byte> hash);
        
    public int SignHash(
        ReadOnlySpan<byte> hash,
        Span<byte> destination,
        DSASignatureFormat signatureFormat);

    public int SignHash(
        ReadOnlySpan<byte> hash,
        Span<byte> destination);
}

@terrajobst terrajobst added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jul 26, 2022
@vcsjones vcsjones self-assigned this Jul 28, 2022
@bartonjs bartonjs modified the milestones: 7.0.0, 8.0.0 Aug 2, 2022
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Aug 5, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Aug 8, 2022
@dotnet dotnet locked as resolved and limited conversation to collaborators Sep 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Security
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants