public override bool TryAggregatePublicKeys(ReadOnlySpan <byte> publicKeys, Span <byte> destination, out int bytesWritten) { // This is independent of the keys set (it uses multiple keys), although other parameters (type of curve, variant, scheme, etc) are relevant. if (publicKeys.Length % PublicKeyLength != 0) { throw new ArgumentOutOfRangeException(nameof(publicKeys), publicKeys.Length, $"Public key data must be a multiple of the public key length {PublicKeyLength}."); } if (destination.Length < PublicKeyLength) { bytesWritten = 0; return(false); } EnsureInitialised(); var aggregateBlsPublicKey = default(Bls384Interop.BlsPublicKey); for (var index = 0; index < publicKeys.Length; index += PublicKeyLength) { var publicKeySlice = publicKeys.Slice(index, PublicKeyLength); var blsPublicKey = default(Bls384Interop.BlsPublicKey); int publicKeyBytesRead; unsafe { // Using fixed pointer for input data allows us to pass a slice fixed(byte *publicKeyPtr = publicKeySlice) { publicKeyBytesRead = Bls384Interop.PublicKeyDeserialize(ref blsPublicKey, publicKeyPtr, PublicKeyLength); } } if (publicKeyBytesRead != PublicKeyLength) { throw new Exception($"Error deserializing BLS public key, length: {publicKeyBytesRead}"); } if (index == 0) { aggregateBlsPublicKey = blsPublicKey; } else { Bls384Interop.PublicKeyAdd(ref aggregateBlsPublicKey, ref blsPublicKey); } } unsafe { // Using fixed pointer for output data allows us to write directly to destination fixed(byte *destinationPtr = destination) { bytesWritten = Bls384Interop.PublicKeySerialize(destinationPtr, PublicKeyLength, ref aggregateBlsPublicKey); } } if (bytesWritten != PublicKeyLength) { throw new Exception($"Error serializing BLS public key, length: {bytesWritten}"); } return(true); }