|  | 
| 2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
| 3 | 3 | 
 | 
| 4 | 4 | using System.Diagnostics; | 
|  | 5 | +using System.Formats.Asn1; | 
| 5 | 6 | 
 | 
| 6 | 7 | namespace System.Security.Cryptography | 
| 7 | 8 | { | 
| 8 | 9 |     internal static partial class AsymmetricAlgorithmHelpers | 
| 9 | 10 |     { | 
|  | 11 | +        /// <summary> | 
|  | 12 | +        /// Convert Ieee1363 format of (r, s) to Der format | 
|  | 13 | +        /// </summary> | 
|  | 14 | +        public static byte[] ConvertIeee1363ToDer(ReadOnlySpan<byte> input) | 
|  | 15 | +        { | 
|  | 16 | +            AsnWriter writer = WriteIeee1363ToDer(input); | 
|  | 17 | +            return writer.Encode(); | 
|  | 18 | +        } | 
|  | 19 | + | 
|  | 20 | +        internal static bool TryConvertIeee1363ToDer( | 
|  | 21 | +            ReadOnlySpan<byte> input, | 
|  | 22 | +            Span<byte> destination, | 
|  | 23 | +            out int bytesWritten) | 
|  | 24 | +        { | 
|  | 25 | +            AsnWriter writer = WriteIeee1363ToDer(input); | 
|  | 26 | +            return writer.TryEncode(destination, out bytesWritten); | 
|  | 27 | +        } | 
|  | 28 | + | 
|  | 29 | +        private static AsnWriter WriteIeee1363ToDer(ReadOnlySpan<byte> input) | 
|  | 30 | +        { | 
|  | 31 | +            Debug.Assert(input.Length % 2 == 0); | 
|  | 32 | +            Debug.Assert(input.Length > 1); | 
|  | 33 | + | 
|  | 34 | +            // Input is (r, s), each of them exactly half of the array. | 
|  | 35 | +            // Output is the DER encoded value of SEQUENCE(INTEGER(r), INTEGER(s)). | 
|  | 36 | +            int halfLength = input.Length / 2; | 
|  | 37 | + | 
|  | 38 | +            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); | 
|  | 39 | +            writer.PushSequence(); | 
|  | 40 | +            writer.WriteKeyParameterInteger(input.Slice(0, halfLength)); | 
|  | 41 | +            writer.WriteKeyParameterInteger(input.Slice(halfLength, halfLength)); | 
|  | 42 | +            writer.PopSequence(); | 
|  | 43 | +            return writer; | 
|  | 44 | +        } | 
|  | 45 | + | 
|  | 46 | +        /// <summary> | 
|  | 47 | +        /// Convert Der format of (r, s) to Ieee1363 format | 
|  | 48 | +        /// </summary> | 
|  | 49 | +        public static byte[] ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits) | 
|  | 50 | +        { | 
|  | 51 | +            int fieldSizeBytes = BitsToBytes(fieldSizeBits); | 
|  | 52 | +            int encodedSize = 2 * fieldSizeBytes; | 
|  | 53 | +            byte[] response = new byte[encodedSize]; | 
|  | 54 | + | 
|  | 55 | +            ConvertDerToIeee1363(input, fieldSizeBits, response); | 
|  | 56 | +            return response; | 
|  | 57 | +        } | 
|  | 58 | + | 
|  | 59 | +        internal static int ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits, Span<byte> destination) | 
|  | 60 | +        { | 
|  | 61 | +            int fieldSizeBytes = BitsToBytes(fieldSizeBits); | 
|  | 62 | +            int encodedSize = 2 * fieldSizeBytes; | 
|  | 63 | + | 
|  | 64 | +            Debug.Assert(destination.Length >= encodedSize); | 
|  | 65 | + | 
|  | 66 | +            try | 
|  | 67 | +            { | 
|  | 68 | +                AsnValueReader reader = new AsnValueReader(input, AsnEncodingRules.DER); | 
|  | 69 | +                AsnValueReader sequenceReader = reader.ReadSequence(); | 
|  | 70 | +                reader.ThrowIfNotEmpty(); | 
|  | 71 | +                ReadOnlySpan<byte> rDer = sequenceReader.ReadIntegerBytes(); | 
|  | 72 | +                ReadOnlySpan<byte> sDer = sequenceReader.ReadIntegerBytes(); | 
|  | 73 | +                sequenceReader.ThrowIfNotEmpty(); | 
|  | 74 | + | 
|  | 75 | +                CopySignatureField(rDer, destination.Slice(0, fieldSizeBytes)); | 
|  | 76 | +                CopySignatureField(sDer, destination.Slice(fieldSizeBytes, fieldSizeBytes)); | 
|  | 77 | +                return encodedSize; | 
|  | 78 | +            } | 
|  | 79 | +            catch (AsnContentException e) | 
|  | 80 | +            { | 
|  | 81 | +                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); | 
|  | 82 | +            } | 
|  | 83 | +        } | 
|  | 84 | + | 
|  | 85 | +        private static void CopySignatureField(ReadOnlySpan<byte> signatureField, Span<byte> response) | 
|  | 86 | +        { | 
|  | 87 | +            if (signatureField.Length > response.Length) | 
|  | 88 | +            { | 
|  | 89 | +                if (signatureField.Length != response.Length + 1 || | 
|  | 90 | +                    signatureField[0] != 0 || | 
|  | 91 | +                    signatureField[1] <= 0x7F) | 
|  | 92 | +                { | 
|  | 93 | +                    // The only way this should be true is if the value required a zero-byte-pad. | 
|  | 94 | +                    Debug.Fail($"A signature field was longer ({signatureField.Length}) than expected ({response.Length})"); | 
|  | 95 | +                    throw new CryptographicException(); | 
|  | 96 | +                } | 
|  | 97 | + | 
|  | 98 | +                signatureField = signatureField.Slice(1); | 
|  | 99 | +            } | 
|  | 100 | + | 
|  | 101 | +            // If the field is too short then it needs to be prepended | 
|  | 102 | +            // with zeroes in the response. | 
|  | 103 | +            int writeOffset = response.Length - signatureField.Length; | 
|  | 104 | +            response.Slice(0, writeOffset).Clear(); | 
|  | 105 | +            signatureField.CopyTo(response.Slice(writeOffset)); | 
|  | 106 | +        } | 
|  | 107 | + | 
| 10 | 108 |         internal static int BitsToBytes(int bitLength) | 
| 11 | 109 |         { | 
| 12 | 110 |             int byteLength = (bitLength + 7) / 8; | 
|  | 
0 commit comments