Skip to content
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
82 changes: 82 additions & 0 deletions UnitTests/UnitTests/ImportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
//
// SPDX-License-Identifier: MIT

using System.Formats.Asn1;
using System.Security.Cryptography;
using Dorssel.Security.Cryptography;
using static Dorssel.Security.Cryptography.X509Certificates.XmssCertificateExtensions;

namespace UnitTests;

Expand Down Expand Up @@ -140,6 +142,17 @@ public void ImportRfcPublicKey_WrongSize()
});
}

[TestMethod]
public void ImportRfcPublicKey_Oversized()
{
using var xmss = new Xmss();

var oversized = new ReadOnlySpan<byte>([..ExampleCertificate.RfcPublicKey.Span, 0]);

xmss.ImportRfcPublicKey(oversized, out var bytesRead);
Assert.AreEqual(ExampleCertificate.RfcPublicKey.Length, bytesRead);
}

[TestMethod]
public void ImportRfcPublicKey_AfterPrivateKey()
{
Expand Down Expand Up @@ -189,6 +202,55 @@ public void ImportAsnPublicKey_Invalid()
});
}

[TestMethod]
public void ImportAsnPublicKey_OversizedAsn()
{
byte[] asn;
{
using var tmpXmss = new Xmss();
tmpXmss.ImportFromPem(ExampleCertificate.Pem);
asn = tmpXmss.ExportAsnPublicKey();
}

using var xmss = new Xmss();

Assert.IsFalse(xmss.HasPrivateKey);
Assert.IsFalse(xmss.HasPublicKey);

xmss.ImportAsnPublicKey([..asn, 0], out var bytesRead);

Assert.AreEqual(asn.Length, bytesRead);
Assert.IsFalse(xmss.HasPrivateKey);
Assert.IsTrue(xmss.HasPublicKey);
}

[TestMethod]
public void ImportAsnPublicKey_OversizedKey()
{
byte[] asn;
{

using var tmpXmss = new Xmss();
tmpXmss.ImportFromPem(ExampleCertificate.Pem);
asn = tmpXmss.ExportAsnPublicKey();
}
var correctKey = AsnDecoder.ReadOctetString(asn, AsnEncodingRules.BER, out var bytesConsumed);

var asnWriter = new AsnWriter(AsnEncodingRules.DER);
asnWriter.WriteOctetString([.. correctKey, 42]);
var asnWithOversizedKey = asnWriter.Encode();

using var xmss = new Xmss();

Assert.IsFalse(xmss.HasPrivateKey);
Assert.IsFalse(xmss.HasPublicKey);

Assert.ThrowsExactly<CryptographicException>(() =>
{
xmss.ImportAsnPublicKey(asnWithOversizedKey, out var bytesRead);
});
}

[TestMethod]
public void ImportSubjectPublicKeyInfo()
{
Expand Down Expand Up @@ -282,6 +344,26 @@ public void ImportFromPem_XmssAsn()
Assert.IsTrue(xmss.HasPublicKey);
}

[TestMethod]
public void ImportFromPem_XmssAsn_OversizedAsn()
{
using var xmss = ExampleCertificate.Certificate2.GetXmssPublicKey()!;

#pragma warning disable CS0618 // Type or member is obsolete
var asnPem = xmss.ExportAsnPublicKeyPem();
#pragma warning restore CS0618 // Type or member is obsolete

var fields = PemEncoding.Find(asnPem);
var correctData = Convert.FromBase64String(asnPem[fields.Base64Data]);

var infoPemWithExtraneousData = PemEncoding.WriteString(asnPem[fields.Label], [.. correctData, 42]);

Assert.ThrowsExactly<CryptographicException>(() =>
{
xmss.ImportFromPem(infoPemWithExtraneousData);
});
}

[TestMethod]
public void ImportFromPem_SubjectPublicKeyInfo()
{
Expand Down
49 changes: 15 additions & 34 deletions Xmss/Xmss.cs
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ public bool Verify(Stream data, ReadOnlySpan<byte> signature)
var buffer = possiblyOversizedBuffer.AsSpan(0, 15 * 1088);
fixed (byte* signaturePtr = signature)
fixed (byte* bufferPtr = buffer)
fixed (XmssPublicKey* publicKeyPtr = &PublicKey)
fixed (XmssPublicKey* publicKeyPin = &PublicKey)
{
var result = UnsafeNativeMethods.xmss_verification_init(out var context, PublicKey, *(XmssSignature*)signaturePtr, (nuint)signature.Length);
if (result == XmssError.XMSS_ERR_INVALID_SIGNATURE)
Expand Down Expand Up @@ -860,7 +860,7 @@ public bool Verify(ReadOnlySpan<byte> data, ReadOnlySpan<byte> signature)
{
fixed (byte* signaturePtr = signature)
fixed (byte* dataPtr = data)
fixed (XmssPublicKey* publicKeyPtr = &PublicKey)
fixed (XmssPublicKey* publicKeyPin = &PublicKey)
{
var result = UnsafeNativeMethods.xmss_verification_init(out var context, PublicKey, *(XmssSignature*)signaturePtr, (nuint)signature.Length);
if (result == XmssError.XMSS_ERR_INVALID_SIGNATURE)
Expand Down Expand Up @@ -957,14 +957,7 @@ public bool TryExportRfcPublicKey(Span<byte> destination, out int bytesWritten)
bytesWritten = 0;
return false;
}
unsafe
{
fixed (XmssPublicKey* publicKeyPtr = &PublicKey)
fixed (byte* destinationPtr = destination)
{
Buffer.MemoryCopy(publicKeyPtr, destinationPtr, destination.Length, Defines.XMSS_PUBLIC_KEY_SIZE);
}
}
MemoryMarshal.Write(destination, PublicKey);
bytesWritten = Defines.XMSS_PUBLIC_KEY_SIZE;
return true;
}
Expand All @@ -981,15 +974,7 @@ public bool TryExportAsnPublicKey(Span<byte> destination, out int bytesWritten)
ThrowIfNoPublicKey();

var asnWriter = new AsnWriter(AsnEncodingRules.DER);
{
unsafe
{
fixed (XmssPublicKey* publicKeyPtr = &PublicKey)
{
asnWriter.WriteOctetString(new(publicKeyPtr, sizeof(XmssPublicKey)));
}
}
}
asnWriter.WriteOctetString(MemoryMarshal.Cast<XmssPublicKey, byte>(new(ref PublicKey)));
return asnWriter.TryEncode(destination, out bytesWritten);
}

Expand Down Expand Up @@ -1027,13 +1012,7 @@ public override bool TryExportSubjectPublicKeyInfo(Span<byte> destination, out i
asnWriter.WriteObjectIdentifier(IdAlgXmssHashsig.Value!);
// PARAMS ARE absent
}
unsafe
{
fixed (XmssPublicKey* publicKeyPtr = &PublicKey)
{
asnWriter.WriteBitString(new(publicKeyPtr, sizeof(XmssPublicKey)));
}
}
asnWriter.WriteBitString(MemoryMarshal.Cast<XmssPublicKey, byte>(new(ref PublicKey)));
}
return asnWriter.TryEncode(destination, out bytesWritten);
}
Expand Down Expand Up @@ -1061,17 +1040,15 @@ static void DecodeXmssPublicKey(ReadOnlySpan<byte> source, out XmssPublicKey pub
{
throw new CryptographicException($"Unsupported parameter set ({parameterSetOID}).");
}
if (source.Length != Defines.XMSS_PUBLIC_KEY_SIZE)
if (source.Length < Defines.XMSS_PUBLIC_KEY_SIZE)
{
throw new CryptographicException("Key value wrong size.");
throw new CryptographicException("Key value too short.");
}
unsafe
if (exact && (source.Length != Defines.XMSS_PUBLIC_KEY_SIZE))
{
fixed (XmssPublicKey* xmssPublicKeyPtr = &publicKey)
{
source[..sizeof(XmssPublicKey)].CopyTo(new Span<byte>(xmssPublicKeyPtr, Defines.XMSS_PUBLIC_KEY_SIZE));
}
throw new CryptographicException("Key value wrong size.");
}
XmssException.ThrowFaultDetectedIf(!MemoryMarshal.TryRead(source, out publicKey));
bytesRead = Defines.XMSS_PUBLIC_KEY_SIZE;
parameterSet = (XmssParameterSet)parameterSetOID;
}
Expand All @@ -1085,13 +1062,17 @@ static void DecodeAsnPublicKey(ReadOnlySpan<byte> source, out XmssPublicKey publ
try
{
xmssPublicKeyData = AsnDecoder.ReadOctetString(source, AsnEncodingRules.BER, out bytesConsumed);
if (exact && (source.Length != bytesConsumed))
{
throw new CryptographicException("ASN.1 wrong size.");
}
}
catch (AsnContentException ex)
{
throw new CryptographicException("Invalid ASN.1 format.", ex);
}

DecodeXmssPublicKey(xmssPublicKeyData, out publicKey, out parameterSet, out _, exact);
DecodeXmssPublicKey(xmssPublicKeyData, out publicKey, out parameterSet, out _, true);
bytesRead = bytesConsumed;
}

Expand Down