@@ -9,8 +9,9 @@ namespace Discord | |||
{ | |||
private string _name; | |||
private object _value; | |||
/// <summary> | |||
/// The name of this choice. | |||
/// Gets the name of this choice. | |||
/// </summary> | |||
public string Name | |||
{ | |||
@@ -24,9 +25,9 @@ namespace Discord | |||
} | |||
/// <summary> | |||
/// The value of this choice. | |||
/// Gets the value of this choice. | |||
/// <note type="warning"> | |||
/// Discord only accepts int and string as the input. | |||
/// Discord only accepts int, double/floats, and string as the input. | |||
/// </note> | |||
/// </summary> | |||
public object Value | |||
@@ -13,7 +13,7 @@ namespace Discord | |||
public Optional<string> Name { get; set; } | |||
/// <summary> | |||
/// Whether the command is enabled by default when the app is added to a guild. Default is <see langword="true"/> | |||
/// Gets whether the command is enabled by default when the app is added to a guild. Default is <see langword="true"/>. | |||
/// </summary> | |||
public Optional<bool> DefaultPermission { get; set; } | |||
@@ -6,7 +6,7 @@ namespace Discord | |||
public class AutocompleteOption | |||
{ | |||
/// <summary> | |||
/// Gets the type of this option | |||
/// Gets the type of this option. | |||
/// </summary> | |||
public ApplicationCommandOptionType Type { get; } | |||
@@ -45,15 +45,13 @@ namespace Discord | |||
public object Value | |||
{ | |||
get => _value; | |||
set => _value = value switch | |||
set | |||
{ | |||
string str => str, | |||
int integer => integer, | |||
long lng => lng, | |||
double number => number, | |||
null => throw new ArgumentNullException(nameof(value), $"{nameof(Value)} cannot be null."), | |||
_ => throw new ArgumentException($"Type {value.GetType().Name} cannot be set as a value! Only string, int, and double allowed!") | |||
}; | |||
if (value is not string || !value.IsNumericType()) | |||
throw new ArgumentException($"{nameof(value)} must be a numeric type or a string!"); | |||
_value = value; | |||
} | |||
} | |||
/// <summary> | |||
@@ -15,27 +15,27 @@ namespace Discord | |||
ulong ApplicationId { get; } | |||
/// <summary> | |||
/// The type of the command. | |||
/// Gets the type of the command. | |||
/// </summary> | |||
ApplicationCommandType Type { get; } | |||
/// <summary> | |||
/// The name of the command. | |||
/// Gets the name of the command. | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// The description of the command. | |||
/// Gets the description of the command. | |||
/// </summary> | |||
string Description { get; } | |||
/// <summary> | |||
/// Whether the command is enabled by default when the app is added to a guild. | |||
/// Gets whether the command is enabled by default when the app is added to a guild. | |||
/// </summary> | |||
bool IsDefaultPermission { get; } | |||
/// <summary> | |||
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters. | |||
/// Gets a collection of options for this application command. | |||
/// </summary> | |||
IReadOnlyCollection<IApplicationCommandOption> Options { get; } | |||
@@ -5,20 +5,20 @@ namespace Discord | |||
/// <summary> | |||
/// Represents data of an Interaction Command, see <see href="https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondata"/>. | |||
/// </summary> | |||
public interface IApplicationCommandInteractionData | |||
public interface IApplicationCommandInteractionData : IDiscordInteractionData | |||
{ | |||
/// <summary> | |||
/// The snowflake id of this command. | |||
/// Gets the snowflake id of this command. | |||
/// </summary> | |||
ulong Id { get; } | |||
/// <summary> | |||
/// The name of this command. | |||
/// Gets the name of this command. | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// The params + values from the user. | |||
/// Gets the params + values from the user. | |||
/// </summary> | |||
IReadOnlyCollection<IApplicationCommandInteractionDataOption> Options { get; } | |||
} | |||
@@ -3,30 +3,30 @@ using System.Collections.Generic; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a option group for a command, see <see href="https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondataoption"/>. | |||
/// Represents a option group for a command. | |||
/// </summary> | |||
public interface IApplicationCommandInteractionDataOption | |||
{ | |||
/// <summary> | |||
/// The name of the parameter. | |||
/// Gets the name of the parameter. | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// The value of the pair. | |||
/// Gets the value of the pair. | |||
/// <note> | |||
/// This objects type can be any one of the option types in <see cref="ApplicationCommandOptionType"/> | |||
/// This objects type can be any one of the option types in <see cref="ApplicationCommandOptionType"/>. | |||
/// </note> | |||
/// </summary> | |||
object Value { get; } | |||
/// <summary> | |||
/// The type of this data's option. | |||
/// Gets the type of this data's option. | |||
/// </summary> | |||
ApplicationCommandOptionType Type { get; } | |||
/// <summary> | |||
/// Present if this option is a group or subcommand. | |||
/// Gets the options for this command. | |||
/// </summary> | |||
IReadOnlyCollection<IApplicationCommandInteractionDataOption> Options { get; } | |||
} | |||
@@ -3,57 +3,57 @@ using System.Collections.Generic; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Options for the <see cref="IApplicationCommand"/>, see <see href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoption">The docs</see>. | |||
/// Options for the <see cref="IApplicationCommand"/>. | |||
/// </summary> | |||
public interface IApplicationCommandOption | |||
{ | |||
/// <summary> | |||
/// The type of this <see cref="IApplicationCommandOption"/>. | |||
/// Gets the type of this <see cref="IApplicationCommandOption"/>. | |||
/// </summary> | |||
ApplicationCommandOptionType Type { get; } | |||
/// <summary> | |||
/// The name of this command option, 1-32 character name. | |||
/// Gets the name of this command option. | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// The description of this command option, 1-100 character description. | |||
/// Gets the description of this command option. | |||
/// </summary> | |||
string Description { get; } | |||
/// <summary> | |||
/// The first required option for the user to complete--only one option can be default. | |||
/// Gets whether or not this is the first required option for the user to complete. | |||
/// </summary> | |||
bool? IsDefault { get; } | |||
/// <summary> | |||
/// If the parameter is required or optional, default is <see langword="false"/>. | |||
/// Gets whether or not the parameter is required or optional. | |||
/// </summary> | |||
bool? IsRequired { get; } | |||
/// <summary> | |||
/// The smallest number value the user can input. | |||
/// Gets the smallest number value the user can input. | |||
/// </summary> | |||
double? MinValue { get; } | |||
/// <summary> | |||
/// The largest number value the user can input. | |||
/// Gets the largest number value the user can input. | |||
/// </summary> | |||
double? MaxValue { get; } | |||
/// <summary> | |||
/// Choices for string and int types for the user to pick from. | |||
/// Gets the choices for string and int types for the user to pick from. | |||
/// </summary> | |||
IReadOnlyCollection<IApplicationCommandOptionChoice> Choices { get; } | |||
/// <summary> | |||
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters. | |||
/// Gets the sub-options for this command option. | |||
/// </summary> | |||
IReadOnlyCollection<IApplicationCommandOption> Options { get; } | |||
/// <summary> | |||
/// The allowed channel types for this option. | |||
/// Gets the allowed channel types for this option. | |||
/// </summary> | |||
IReadOnlyCollection<ChannelType> ChannelTypes { get; } | |||
} | |||
@@ -6,12 +6,12 @@ namespace Discord | |||
public interface IApplicationCommandOptionChoice | |||
{ | |||
/// <summary> | |||
/// 1-100 character choice name. | |||
/// Gets the choice name. | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// value of the choice. | |||
/// Gets the value of the choice. | |||
/// </summary> | |||
object Value { get; } | |||
} | |||
@@ -4,36 +4,32 @@ using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a discord interaction | |||
/// <para> | |||
/// An interaction is the base "thing" that is sent when a user invokes a command, and is the same for Slash Commands | |||
/// and other future interaction types. see <see href="https://discord.com/developers/docs/interactions/slash-commands#interaction"/>. | |||
/// </para> | |||
/// Represents a discord interaction. | |||
/// </summary> | |||
public interface IDiscordInteraction : ISnowflakeEntity | |||
{ | |||
/// <summary> | |||
/// The id of the interaction. | |||
/// Gets the id of the interaction. | |||
/// </summary> | |||
new ulong Id { get; } | |||
/// <summary> | |||
/// The type of this <see cref="IDiscordInteraction"/>. | |||
/// Gets the type of this <see cref="IDiscordInteraction"/>. | |||
/// </summary> | |||
InteractionType Type { get; } | |||
/// <summary> | |||
/// Represents the data sent within this interaction. | |||
/// Gets the data sent within this interaction. | |||
/// </summary> | |||
IDiscordInteractionData Data { get; } | |||
/// <summary> | |||
/// A continuation token for responding to the interaction. | |||
/// Gets the continuation token for responding to the interaction. | |||
/// </summary> | |||
string Token { get; } | |||
/// <summary> | |||
/// read-only property, always 1. | |||
/// Gets the version of the interaction, always 1. | |||
/// </summary> | |||
int Version { get; } | |||
@@ -1,8 +1,13 @@ | |||
//using Discord.Rest.Entities.Interactions; | |||
using Discord.Net; | |||
using Discord.Net.Converters; | |||
using Discord.Net.ED25519; | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.Immutable; | |||
using System.IO; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.Rest | |||
@@ -14,12 +19,13 @@ namespace Discord.Rest | |||
{ | |||
#region DiscordRestClient | |||
private RestApplication _applicationInfo; | |||
internal static JsonSerializer Serializer = new JsonSerializer() { ContractResolver = new DiscordContractResolver(), NullValueHandling = NullValueHandling.Include }; | |||
/// <summary> | |||
/// Gets the logged-in user. | |||
/// </summary> | |||
public new RestSelfUser CurrentUser { get => base.CurrentUser as RestSelfUser; internal set => base.CurrentUser = value; } | |||
/// <inheritdoc /> | |||
public DiscordRestClient() : this(new DiscordRestConfig()) { } | |||
/// <summary> | |||
@@ -31,7 +37,7 @@ namespace Discord.Rest | |||
internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient api) : base(config, api) { } | |||
private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | |||
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, useSystemClock: config.UseSystemClock); | |||
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, serializer: Serializer, useSystemClock: config.UseSystemClock); | |||
internal override void Dispose(bool disposing) | |||
{ | |||
@@ -60,6 +66,70 @@ namespace Discord.Rest | |||
return Task.Delay(0); | |||
} | |||
#region Rest interactions | |||
public bool IsValidHttpInteraction(string publicKey, string signature, string timestamp, string body) | |||
=> IsValidHttpInteraction(publicKey, signature, timestamp, Encoding.UTF8.GetBytes(body)); | |||
public bool IsValidHttpInteraction(string publicKey, string signature, string timestamp, byte[] body) | |||
{ | |||
var key = HexConverter.HexToByteArray(publicKey); | |||
var sig = HexConverter.HexToByteArray(signature); | |||
var tsp = Encoding.UTF8.GetBytes(timestamp); | |||
var message = new List<byte>(); | |||
message.AddRange(tsp); | |||
message.AddRange(body); | |||
return IsValidHttpInteraction(key, sig, message.ToArray()); | |||
} | |||
private bool IsValidHttpInteraction(byte[] publicKey, byte[] signature, byte[] message) | |||
{ | |||
return Ed25519.Verify(signature, message, publicKey); | |||
} | |||
/// <summary> | |||
/// Creates a <see cref="RestInteraction"/> from a http message. | |||
/// </summary> | |||
/// <param name="publicKey">The public key of your application</param> | |||
/// <param name="signature">The signature sent with the interaction.</param> | |||
/// <param name="timestamp">The timestamp sent with the interaction.</param> | |||
/// <param name="body">The body of the http message.</param> | |||
/// <returns> | |||
/// A <see cref="RestInteraction"/> that represents the incoming http interaction. | |||
/// </returns> | |||
/// <exception cref="BadSignatureException">Thrown when the signature doesn't match the public key.</exception> | |||
public Task<RestInteraction> ParseHttpInteractionAsync(string publicKey, string signature, string timestamp, string body) | |||
=> ParseHttpInteractionAsync(publicKey, signature, timestamp, Encoding.UTF8.GetBytes(body)); | |||
/// <summary> | |||
/// Creates a <see cref="RestInteraction"/> from a http message. | |||
/// </summary> | |||
/// <param name="publicKey">The public key of your application</param> | |||
/// <param name="signature">The signature sent with the interaction.</param> | |||
/// <param name="timestamp">The timestamp sent with the interaction.</param> | |||
/// <param name="body">The body of the http message.</param> | |||
/// <returns> | |||
/// A <see cref="RestInteraction"/> that represents the incoming http interaction. | |||
/// </returns> | |||
/// <exception cref="BadSignatureException">Thrown when the signature doesn't match the public key.</exception> | |||
public async Task<RestInteraction> ParseHttpInteractionAsync(string publicKey, string signature, string timestamp, byte[] body) | |||
{ | |||
if (!IsValidHttpInteraction(publicKey, signature, timestamp, body)) | |||
{ | |||
throw new BadSignatureException(); | |||
} | |||
using (var textReader = new StringReader(Encoding.UTF8.GetString(body))) | |||
using (var jsonReader = new JsonTextReader(textReader)) | |||
{ | |||
var model = Serializer.Deserialize<API.Interaction>(jsonReader); | |||
return await RestInteraction.CreateAsync(this, model); | |||
} | |||
} | |||
#endregion | |||
public async Task<RestApplication> GetApplicationInfoAsync(RequestOptions options = null) | |||
{ | |||
return _applicationInfo ??= await ClientHelper.GetApplicationInfoAsync(this, options).ConfigureAwait(false); | |||
@@ -0,0 +1,354 @@ | |||
using Discord.Net.Rest; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using DataModel = Discord.API.ApplicationCommandInteractionData; | |||
using Model = Discord.API.Interaction; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based base command interaction. | |||
/// </summary> | |||
public class RestCommandBase : RestInteraction | |||
{ | |||
/// <summary> | |||
/// Gets the name of the invoked command. | |||
/// </summary> | |||
public string CommandName | |||
=> Data.Name; | |||
/// <summary> | |||
/// Gets the id of the invoked command. | |||
/// </summary> | |||
public ulong CommandId | |||
=> Data.Id; | |||
/// <summary> | |||
/// The data associated with this interaction. | |||
/// </summary> | |||
internal new RestCommandBaseData Data { get; private set; } | |||
internal override bool _hasResponded { get; set; } | |||
private object _lock = new object(); | |||
internal RestCommandBase(DiscordRestClient client, Model model) | |||
: base(client, model.Id) | |||
{ | |||
} | |||
internal new static async Task<RestCommandBase> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestCommandBase(client, model); | |||
await entity.UpdateAsync(client, model).ConfigureAwait(false); | |||
return entity; | |||
} | |||
internal override async Task UpdateAsync(DiscordRestClient client, Model model) | |||
{ | |||
await base.UpdateAsync(client, model); | |||
var data = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
if(Data == null) | |||
{ | |||
Data = await RestCommandBaseData.CreateAsync(client, data, Guild, Channel).ConfigureAwait(false); | |||
} | |||
} | |||
/// <summary> | |||
/// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||
/// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
public override string Respond( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
// check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
{ | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) && | |||
allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions)); | |||
} | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) && | |||
allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions)); | |||
} | |||
} | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.ChannelMessageWithSource, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
TTS = isTTS, | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified | |||
} | |||
}; | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
return SerializePayload(response); | |||
} | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public override async Task<RestFollowupMessage> FollowupAsync( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
}; | |||
if (ephemeral) | |||
args.Flags = MessageFlags.Ephemeral; | |||
return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
} | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="fileStream">The file to upload.</param> | |||
/// <param name="fileName">The file name of the attachment.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
Stream fileStream, | |||
string fileName, | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | |||
Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified | |||
}; | |||
if (ephemeral) | |||
args.Flags = MessageFlags.Ephemeral; | |||
return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
} | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="filePath">The file to upload.</param> | |||
/// <param name="fileName">The file name of the attachment.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
string filePath, | |||
string text = null, | |||
string fileName = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist"); | |||
fileName ??= Path.GetFileName(filePath); | |||
Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified | |||
}; | |||
if (ephemeral) | |||
args.Flags = MessageFlags.Ephemeral; | |||
return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options); | |||
} | |||
/// <summary> | |||
/// Acknowledges this interaction with the <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>. | |||
/// </summary> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
public override string Defer(bool ephemeral = false, RequestOptions options = null) | |||
{ | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot defer an interaction after {InteractionHelper.ResponseTimeLimit} seconds!"); | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.DeferredChannelMessageWithSource, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified | |||
} | |||
}; | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
return SerializePayload(response); | |||
} | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.ApplicationCommandInteractionData; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents the base data tied with the <see cref="RestCommandBase"/> interaction. | |||
/// </summary> | |||
public class RestCommandBaseData<TOption> : RestEntity<ulong>, IApplicationCommandInteractionData where TOption : IApplicationCommandInteractionDataOption | |||
{ | |||
/// <inheritdoc/> | |||
public string Name { get; private set; } | |||
/// <summary> | |||
/// Gets a collection of <typeparamref name="TOption"/> received with this interaction. | |||
/// </summary> | |||
public virtual IReadOnlyCollection<TOption> Options { get; internal set; } | |||
internal RestResolvableData<Model> ResolvableData; | |||
internal RestCommandBaseData(BaseDiscordClient client, Model model) | |||
: base(client, model.Id) | |||
{ | |||
} | |||
internal static async Task<RestCommandBaseData> CreateAsync(DiscordRestClient client, Model model, IGuild guild, IRestMessageChannel channel) | |||
{ | |||
var entity = new RestCommandBaseData(client, model); | |||
await entity.UpdateAsync(client, model, guild, channel); | |||
return entity; | |||
} | |||
internal virtual async Task UpdateAsync(DiscordRestClient client, Model model, IGuild guild, IRestMessageChannel channel) | |||
{ | |||
Name = model.Name; | |||
if (model.Resolved.IsSpecified && ResolvableData == null) | |||
{ | |||
ResolvableData = new RestResolvableData<Model>(); | |||
await ResolvableData.PopulateAsync(client, guild, channel, model).ConfigureAwait(false); | |||
} | |||
} | |||
IReadOnlyCollection<IApplicationCommandInteractionDataOption> IApplicationCommandInteractionData.Options | |||
=> (IReadOnlyCollection<IApplicationCommandInteractionDataOption>)Options; | |||
} | |||
/// <summary> | |||
/// Represents the base data tied with the <see cref="RestCommandBase"/> interaction. | |||
/// </summary> | |||
public class RestCommandBaseData : RestCommandBaseData<IApplicationCommandInteractionDataOption> | |||
{ | |||
internal RestCommandBaseData(DiscordRestClient client, Model model) | |||
: base(client, model) { } | |||
} | |||
} |
@@ -0,0 +1,92 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.Rest | |||
{ | |||
internal class RestResolvableData<T> where T : API.IResolvable | |||
{ | |||
internal readonly Dictionary<ulong, RestGuildUser> GuildMembers | |||
= new Dictionary<ulong, RestGuildUser>(); | |||
internal readonly Dictionary<ulong, RestUser> Users | |||
= new Dictionary<ulong, RestUser>(); | |||
internal readonly Dictionary<ulong, RestChannel> Channels | |||
= new Dictionary<ulong, RestChannel>(); | |||
internal readonly Dictionary<ulong, RestRole> Roles | |||
= new Dictionary<ulong, RestRole>(); | |||
internal readonly Dictionary<ulong, RestMessage> Messages | |||
= new Dictionary<ulong, RestMessage>(); | |||
internal async Task PopulateAsync(DiscordRestClient discord, IGuild guild, IRestMessageChannel channel, T model) | |||
{ | |||
var resolved = model.Resolved.Value; | |||
if (resolved.Users.IsSpecified) | |||
{ | |||
foreach (var user in resolved.Users.Value) | |||
{ | |||
var restUser = RestUser.Create(discord, user.Value); | |||
Users.Add(ulong.Parse(user.Key), restUser); | |||
} | |||
} | |||
if (resolved.Channels.IsSpecified) | |||
{ | |||
//var channels = await guild.GetChannelsAsync().ConfigureAwait(false); | |||
foreach (var channelModel in resolved.Channels.Value) | |||
{ | |||
var restChannel = RestChannel.Create(discord, channelModel.Value); | |||
Channels.Add(ulong.Parse(channelModel.Key), restChannel); | |||
} | |||
} | |||
if (resolved.Members.IsSpecified) | |||
{ | |||
foreach (var member in resolved.Members.Value) | |||
{ | |||
var restMember = RestGuildUser.Create(discord, guild, member.Value); | |||
GuildMembers.Add(ulong.Parse(member.Key), restMember); | |||
} | |||
} | |||
if (resolved.Roles.IsSpecified) | |||
{ | |||
foreach (var role in resolved.Roles.Value) | |||
{ | |||
var restRole = RestRole.Create(discord, guild, role.Value); | |||
Roles.Add(ulong.Parse(role.Key), restRole); | |||
} | |||
} | |||
if (resolved.Messages.IsSpecified) | |||
{ | |||
foreach (var msg in resolved.Messages.Value) | |||
{ | |||
channel ??= (IRestMessageChannel)(Channels.FirstOrDefault(x => x.Key == msg.Value.ChannelId).Value ?? await discord.GetChannelAsync(msg.Value.ChannelId).ConfigureAwait(false)); | |||
RestUser author; | |||
if (msg.Value.Author.IsSpecified) | |||
{ | |||
author = RestUser.Create(discord, msg.Value.Author.Value); | |||
} | |||
else | |||
{ | |||
author = RestGuildUser.Create(discord, guild, msg.Value.Member.Value); | |||
} | |||
var message = RestMessage.Create(discord, channel, author, msg.Value); | |||
Messages.Add(message.Id, message); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
using System.Threading.Tasks; | |||
using DataModel = Discord.API.ApplicationCommandInteractionData; | |||
using Model = Discord.API.Interaction; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based message command interaction. | |||
/// </summary> | |||
public class RestMessageCommand : RestCommandBase, IDiscordInteraction | |||
{ | |||
/// <summary> | |||
/// The data associated with this interaction. | |||
/// </summary> | |||
public new RestMessageCommandData Data { get; private set; } | |||
internal RestMessageCommand(DiscordRestClient client, Model model) | |||
: base(client, model) | |||
{ | |||
} | |||
internal new static async Task<RestMessageCommand> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestMessageCommand(client, model); | |||
await entity.UpdateAsync(client, model).ConfigureAwait(false); | |||
return entity; | |||
} | |||
internal override async Task UpdateAsync(DiscordRestClient client, Model model) | |||
{ | |||
await base.UpdateAsync(client, model); | |||
var dataModel = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
Data = await RestMessageCommandData.CreateAsync(client, dataModel, Guild, Channel).ConfigureAwait(false); | |||
} | |||
IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.ApplicationCommandInteractionData; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents the data for a <see cref="RestMessageCommand"/>. | |||
/// </summary> | |||
public class RestMessageCommandData : RestCommandBaseData, IDiscordInteractionData | |||
{ | |||
/// <summary> | |||
/// Gets the message associated with this message command. | |||
/// </summary> | |||
public RestMessage Message | |||
=> ResolvableData?.Messages.FirstOrDefault().Value; | |||
/// <inheritdoc/> | |||
/// <remarks> | |||
/// <b>Note</b> Not implemented for <see cref="SocketMessageCommandData"/> | |||
/// </remarks> | |||
public override IReadOnlyCollection<IApplicationCommandInteractionDataOption> Options | |||
=> throw new System.NotImplementedException(); | |||
internal RestMessageCommandData(DiscordRestClient client, Model model) | |||
: base(client, model) { } | |||
internal new static async Task<RestMessageCommandData> CreateAsync(DiscordRestClient client, Model model, IGuild guild, IRestMessageChannel channel) | |||
{ | |||
var entity = new RestMessageCommandData(client, model); | |||
await entity.UpdateAsync(client, model, guild, channel).ConfigureAwait(false); | |||
return entity; | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using DataModel = Discord.API.ApplicationCommandInteractionData; | |||
using Model = Discord.API.Interaction; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based user command. | |||
/// </summary> | |||
internal class RestUserCommand : RestCommandBase, IDiscordInteraction | |||
{ | |||
/// <summary> | |||
/// Gets the data associated with this interaction. | |||
/// </summary> | |||
public new RestUserCommandData Data { get; private set; } | |||
internal RestUserCommand(DiscordRestClient client, Model model) | |||
: base(client, model) | |||
{ | |||
} | |||
internal new static async Task<RestUserCommand> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestUserCommand(client, model); | |||
await entity.UpdateAsync(client, model).ConfigureAwait(false); | |||
return entity; | |||
} | |||
internal override async Task UpdateAsync(DiscordRestClient client, Model model) | |||
{ | |||
await base.UpdateAsync(client, model).ConfigureAwait(false); | |||
var dataModel = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
Data = await RestUserCommandData.CreateAsync(client, dataModel, Guild, Channel).ConfigureAwait(false); | |||
} | |||
IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.ApplicationCommandInteractionData; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents the data for a <see cref="RestUserCommand"/>. | |||
/// </summary> | |||
public class RestUserCommandData : RestCommandBaseData, IDiscordInteractionData | |||
{ | |||
/// <summary> | |||
/// Gets the user who this command targets. | |||
/// </summary> | |||
public RestUser Member | |||
=> (RestUser)ResolvableData.GuildMembers.Values.FirstOrDefault() ?? ResolvableData.Users.Values.FirstOrDefault(); | |||
/// <inheritdoc/> | |||
/// <remarks> | |||
/// <b>Note</b> Not implemented for <see cref="SocketUserCommandData"/> | |||
/// </remarks> | |||
public override IReadOnlyCollection<IApplicationCommandInteractionDataOption> Options | |||
=> throw new System.NotImplementedException(); | |||
internal RestUserCommandData(DiscordRestClient client, Model model) | |||
: base(client, model) { } | |||
internal new static async Task<RestUserCommandData> CreateAsync(DiscordRestClient client, Model model, IGuild guild, IRestMessageChannel channel) | |||
{ | |||
var entity = new RestUserCommandData(client, model); | |||
await entity.UpdateAsync(client, model, guild, channel).ConfigureAwait(false); | |||
return entity; | |||
} | |||
} | |||
} |
@@ -0,0 +1,447 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Interaction; | |||
using DataModel = Discord.API.MessageComponentInteractionData; | |||
using System.IO; | |||
using Discord.Net.Rest; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based message component. | |||
/// </summary> | |||
internal class RestMessageComponent : RestInteraction, IDiscordInteraction | |||
{ | |||
/// <summary> | |||
/// Gets the data received with this interaction, contains the button that was clicked. | |||
/// </summary> | |||
public new RestMessageComponentData Data { get; } | |||
/// <summary> | |||
/// Gets the message that contained the trigger for this interaction. | |||
/// </summary> | |||
public RestUserMessage Message { get; private set; } | |||
private object _lock = new object(); | |||
internal override bool _hasResponded { get; set; } = false; | |||
internal RestMessageComponent(BaseDiscordClient client, Model model) | |||
: base(client, model.Id) | |||
{ | |||
var dataModel = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
Data = new RestMessageComponentData(dataModel); | |||
} | |||
internal new static async Task<RestMessageComponent> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestMessageComponent(client, model); | |||
await entity.UpdateAsync(client, model); | |||
return entity; | |||
} | |||
internal override async Task UpdateAsync(DiscordRestClient discord, Model model) | |||
{ | |||
await base.UpdateAsync(discord, model); | |||
if (model.Message.IsSpecified && model.ChannelId.IsSpecified) | |||
{ | |||
if (Message == null) | |||
{ | |||
Message = RestUserMessage.Create(Discord, Channel, User, model.Message.Value); | |||
} | |||
} | |||
} | |||
/// <summary> | |||
/// Responds to an Interaction with type <see cref="InteractionResponseType.ChannelMessageWithSource"/>. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
public override string Respond( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
// check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||
{ | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) && | |||
allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions)); | |||
} | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) && | |||
allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions)); | |||
} | |||
} | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.ChannelMessageWithSource, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Content = text ?? Optional<string>.Unspecified, | |||
AllowedMentions = allowedMentions?.ToModel(), | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
TTS = isTTS, | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
} | |||
}; | |||
if (ephemeral) | |||
response.Data.Value.Flags = MessageFlags.Ephemeral; | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
return SerializePayload(response); | |||
} | |||
/// <summary> | |||
/// Updates the message which this component resides in with the type <see cref="InteractionResponseType.UpdateMessage"/> | |||
/// </summary> | |||
/// <param name="func">A delegate containing the properties to modify the message with.</param> | |||
/// <param name="options">The request options for this <see langword="async"/> request.</param> | |||
/// <returns>A string that contains json to write back to the incoming http request.</returns> | |||
public string Update(Action<MessageProperties> func, RequestOptions options = null) | |||
{ | |||
var args = new MessageProperties(); | |||
func(args); | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!"); | |||
if (args.AllowedMentions.IsSpecified) | |||
{ | |||
var allowedMentions = args.AllowedMentions.Value; | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions), "A max of 100 user Ids are allowed."); | |||
} | |||
var embed = args.Embed; | |||
var embeds = args.Embeds; | |||
bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(Message.Content); | |||
bool hasEmbeds = embed.IsSpecified && embed.Value != null || embeds.IsSpecified && embeds.Value?.Length > 0 || Message.Embeds.Any(); | |||
if (!hasText && !hasEmbeds) | |||
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); | |||
var apiEmbeds = embed.IsSpecified || embeds.IsSpecified ? new List<API.Embed>() : null; | |||
if (embed.IsSpecified && embed.Value != null) | |||
{ | |||
apiEmbeds.Add(embed.Value.ToModel()); | |||
} | |||
if (embeds.IsSpecified && embeds.Value != null) | |||
{ | |||
apiEmbeds.AddRange(embeds.Value.Select(x => x.ToModel())); | |||
} | |||
Preconditions.AtMost(apiEmbeds?.Count ?? 0, 10, nameof(args.Embeds), "A max of 10 embeds are allowed."); | |||
// check that user flag and user Id list are exclusive, same with role flag and role Id list | |||
if (args.AllowedMentions.IsSpecified && args.AllowedMentions.Value != null && args.AllowedMentions.Value.AllowedTypes.HasValue) | |||
{ | |||
var allowedMentions = args.AllowedMentions.Value; | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) | |||
&& allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(args.AllowedMentions)); | |||
} | |||
if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) | |||
&& allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||
{ | |||
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(args.AllowedMentions)); | |||
} | |||
} | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.UpdateMessage, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Content = args.Content, | |||
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified, | |||
Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified, | |||
Components = args.Components.IsSpecified | |||
? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>() | |||
: Optional<API.ActionRowComponent[]>.Unspecified, | |||
Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified | |||
} | |||
}; | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond, update, or defer twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
return SerializePayload(response); | |||
} | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public override async Task<RestFollowupMessage> FollowupAsync( | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||
}; | |||
if (ephemeral) | |||
args.Flags = MessageFlags.Ephemeral; | |||
return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Message.Channel, options).ConfigureAwait(false); | |||
} | |||
/// <inheritdoc/> | |||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
Stream fileStream, | |||
string fileName, | |||
string text = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
Preconditions.NotNull(fileStream, nameof(fileStream), "File Stream must have data"); | |||
Preconditions.NotNullOrWhitespace(fileName, nameof(fileName), "File Name must not be empty or null"); | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified | |||
}; | |||
if (ephemeral) | |||
args.Flags = MessageFlags.Ephemeral; | |||
return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Message.Channel, options).ConfigureAwait(false); | |||
} | |||
/// <inheritdoc/> | |||
public override async Task<RestFollowupMessage> FollowupWithFileAsync( | |||
string filePath, | |||
string text = null, | |||
string fileName = null, | |||
Embed[] embeds = null, | |||
bool isTTS = false, | |||
bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, | |||
RequestOptions options = null, | |||
MessageComponent component = null, | |||
Embed embed = null) | |||
{ | |||
if (!IsValidToken) | |||
throw new InvalidOperationException("Interaction token is no longer valid"); | |||
embeds ??= Array.Empty<Embed>(); | |||
if (embed != null) | |||
embeds = new[] { embed }.Concat(embeds).ToArray(); | |||
Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed."); | |||
Preconditions.NotNullOrWhitespace(filePath, nameof(filePath), "Path must exist"); | |||
var args = new API.Rest.CreateWebhookMessageParams | |||
{ | |||
Content = text, | |||
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, | |||
IsTTS = isTTS, | |||
Embeds = embeds.Select(x => x.ToModel()).ToArray(), | |||
Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, | |||
File = !string.IsNullOrEmpty(filePath) ? new MultipartFile(new MemoryStream(File.ReadAllBytes(filePath), false), fileName) : Optional<MultipartFile>.Unspecified | |||
}; | |||
if (ephemeral) | |||
args.Flags = MessageFlags.Ephemeral; | |||
return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Message.Channel, options).ConfigureAwait(false); | |||
} | |||
/// <summary> | |||
/// Defers an interaction and responds with type 5 (<see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>) | |||
/// </summary> | |||
/// <param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param> | |||
/// <param name="options">The request options for this <see langword="async"/> request.</param> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
public string DeferLoading(bool ephemeral = false, RequestOptions options = null) | |||
{ | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot defer an interaction after {InteractionHelper.ResponseTimeLimit} seconds of no response/acknowledgement"); | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.DeferredChannelMessageWithSource, | |||
Data = ephemeral ? new API.InteractionCallbackData { Flags = MessageFlags.Ephemeral } : Optional<API.InteractionCallbackData>.Unspecified | |||
}; | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
return SerializePayload(response); | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="ephemeral"></param> | |||
/// <param name="options"></param> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
/// <exception cref="TimeoutException"></exception> | |||
/// <exception cref="InvalidOperationException"></exception> | |||
public override string Defer(bool ephemeral = false, RequestOptions options = null) | |||
{ | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot defer an interaction after {InteractionHelper.ResponseTimeLimit} seconds of no response/acknowledgement"); | |||
var response = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.DeferredUpdateMessage, | |||
Data = ephemeral ? new API.InteractionCallbackData { Flags = MessageFlags.Ephemeral } : Optional<API.InteractionCallbackData>.Unspecified | |||
}; | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond or defer twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
return SerializePayload(response); | |||
} | |||
IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.MessageComponentInteractionData; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents data for a <see cref="RestMessageComponent"/>. | |||
/// </summary> | |||
public class RestMessageComponentData : IDiscordInteractionData | |||
{ | |||
/// <summary> | |||
/// Gets the components Custom Id that was clicked. | |||
/// </summary> | |||
public string CustomId { get; } | |||
/// <summary> | |||
/// Gets the type of the component clicked. | |||
/// </summary> | |||
public ComponentType Type { get; } | |||
/// <summary> | |||
/// Gets the value(s) of a <see cref="SelectMenuComponent"/> interaction response. | |||
/// </summary> | |||
public IReadOnlyCollection<string> Values { get; } | |||
internal RestMessageComponentData(Model model) | |||
{ | |||
CustomId = model.CustomId; | |||
Type = model.ComponentType; | |||
Values = model.Values.GetValueOrDefault(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,222 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Interaction; | |||
using DataModel = Discord.API.ApplicationCommandInteractionData; | |||
using Newtonsoft.Json; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based interaction. | |||
/// </summary> | |||
public abstract class RestInteraction : RestEntity<ulong>, IDiscordInteraction | |||
{ | |||
/// <inheritdoc/> | |||
public InteractionType Type { get; private set; } | |||
/// <inheritdoc/> | |||
public IDiscordInteractionData Data { get; private set; } | |||
/// <inheritdoc/> | |||
public string Token { get; private set; } | |||
/// <inheritdoc/> | |||
public int Version { get; private set; } | |||
/// <summary> | |||
/// Gets the user who invoked the interaction. | |||
/// </summary> | |||
public RestUser User { get; private set; } | |||
/// <inheritdoc/> | |||
public DateTimeOffset CreatedAt | |||
=> SnowflakeUtils.FromSnowflake(Id); | |||
internal abstract bool _hasResponded { get; set; } | |||
/// <summary> | |||
/// <see langword="true"/> if the token is valid for replying to, otherwise <see langword="false"/>. | |||
/// </summary> | |||
public bool IsValidToken | |||
=> InteractionHelper.CanRespondOrFollowup(this); | |||
/// <summary> | |||
/// Gets the channel that this interaction was executed in. | |||
/// </summary> | |||
public IRestMessageChannel Channel { get; private set; } | |||
/// <summary> | |||
/// Gets the guild this interaction was executed in. | |||
/// </summary> | |||
public RestGuild Guild { get; private set; } | |||
internal RestInteraction(BaseDiscordClient discord, ulong id) | |||
: base(discord, id) | |||
{ | |||
} | |||
internal static async Task<RestInteraction> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
if(model.Type == InteractionType.Ping) | |||
{ | |||
return await RestPingInteraction.CreateAsync(client, model); | |||
} | |||
if (model.Type == InteractionType.ApplicationCommand) | |||
{ | |||
var dataModel = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
if (dataModel == null) | |||
return null; | |||
return dataModel.Type switch | |||
{ | |||
ApplicationCommandType.Slash => await RestSlashCommand.CreateAsync(client, model).ConfigureAwait(false), | |||
ApplicationCommandType.Message => await RestMessageCommand.CreateAsync(client, model).ConfigureAwait(false), | |||
ApplicationCommandType.User => await RestUserCommand.CreateAsync(client, model).ConfigureAwait(false), | |||
_ => null | |||
}; | |||
} | |||
if (model.Type == InteractionType.MessageComponent) | |||
return await RestMessageComponent.CreateAsync(client, model).ConfigureAwait(false); | |||
if (model.Type == InteractionType.ApplicationCommandAutocomplete) | |||
return await RestAutocompleteInteraction.CreateAsync(client, model).ConfigureAwait(false); | |||
return null; | |||
} | |||
internal virtual async Task UpdateAsync(DiscordRestClient discord, Model model) | |||
{ | |||
Data = model.Data.IsSpecified | |||
? model.Data.Value | |||
: null; | |||
Token = model.Token; | |||
Version = model.Version; | |||
Type = model.Type; | |||
if(Guild == null && model.GuildId.IsSpecified) | |||
{ | |||
Guild = await discord.GetGuildAsync(model.GuildId.Value); | |||
} | |||
if (User == null) | |||
{ | |||
if (model.Member.IsSpecified && model.GuildId.IsSpecified) | |||
{ | |||
User = RestGuildUser.Create(Discord, Guild, model.Member.Value); | |||
} | |||
else | |||
{ | |||
User = RestUser.Create(Discord, model.User.Value); | |||
} | |||
} | |||
if(Channel == null && model.ChannelId.IsSpecified) | |||
{ | |||
Channel = (IRestMessageChannel)await discord.GetChannelAsync(model.ChannelId.Value); | |||
} | |||
} | |||
internal string SerializePayload(object payload) | |||
{ | |||
var json = new StringBuilder(); | |||
using (var text = new StringWriter(json)) | |||
using (var writer = new JsonTextWriter(text)) | |||
DiscordRestClient.Serializer.Serialize(writer, payload); | |||
return json.ToString(); | |||
} | |||
/// <inheritdoc/> | |||
public abstract string Defer(bool ephemeral = false, RequestOptions options = null); | |||
/// <inheritdoc/> | |||
public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
/// <summary> | |||
/// Gets the original response for this interaction. | |||
/// </summary> | |||
/// <param name="options">The request options for this <see langword="async"/> request.</param> | |||
/// <returns>A <see cref="RestInteractionMessage"/> that represents the initial response.</returns> | |||
public Task<RestInteractionMessage> GetOriginalResponseAsync(RequestOptions options = null) | |||
=> InteractionHelper.GetOriginalResponseAsync(Discord, Channel, this, options); | |||
/// <summary> | |||
/// Edits original response for this interaction. | |||
/// </summary> | |||
/// <param name="func">A delegate containing the properties to modify the message with.</param> | |||
/// <param name="options">The request options for this <see langword="async"/> request.</param> | |||
/// <returns>A <see cref="RestInteractionMessage"/> that represents the initial response.</returns> | |||
public async Task<RestInteractionMessage> ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options = null) | |||
{ | |||
var model = await InteractionHelper.ModifyInteractionResponseAsync(Discord, Token, func, options); | |||
return RestInteractionMessage.Create(Discord, model, Token, Channel); | |||
} | |||
/// <inheritdoc/> | |||
public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="fileStream">The file to upload.</param> | |||
/// <param name="fileName">The file name of the attachment.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
/// <summary> | |||
/// Sends a followup message for this interaction. | |||
/// </summary> | |||
/// <param name="text">The text of the message to be sent.</param> | |||
/// <param name="filePath">The file to upload.</param> | |||
/// <param name="fileName">The file name of the attachment.</param> | |||
/// <param name="embeds">A array of embeds to send with this response. Max 10.</param> | |||
/// <param name="isTTS"><see langword="true"/> if the message should be read out by a text-to-speech reader, otherwise <see langword="false"/>.</param> | |||
/// <param name="ephemeral"><see langword="true"/> if the response should be hidden to everyone besides the invoker of the command, otherwise <see langword="false"/>.</param> | |||
/// <param name="allowedMentions">The allowed mentions for this response.</param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="component">A <see cref="MessageComponent"/> to be sent with this response.</param> | |||
/// <param name="embed">A single embed to send with this response. If this is passed alongside an array of embeds, the single embed will be ignored.</param> | |||
/// <returns> | |||
/// The sent message. | |||
/// </returns> | |||
public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, | |||
AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null); | |||
#region IDiscordInteraction | |||
/// <inheritdoc/> | |||
Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, RequestOptions options, MessageComponent component, Embed embed) | |||
=> Task.FromResult(Respond(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed)); | |||
Task IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options) | |||
=> Task.FromResult(Defer(ephemeral, options)); | |||
/// <inheritdoc/> | |||
async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, | |||
RequestOptions options, MessageComponent component, Embed embed) | |||
=> await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, options, component, embed).ConfigureAwait(false); | |||
/// <inheritdoc/> | |||
async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options) | |||
=> await GetOriginalResponseAsync(options).ConfigureAwait(false); | |||
/// <inheritdoc/> | |||
async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options) | |||
=> await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false); | |||
#endregion | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Interaction; | |||
namespace Discord.Rest | |||
{ | |||
public class RestPingInteraction : RestInteraction, IDiscordInteraction | |||
{ | |||
internal override bool _hasResponded { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | |||
public RestPingInteraction(BaseDiscordClient client, ulong id) | |||
: base(client, id) | |||
{ | |||
} | |||
internal static new async Task<RestPingInteraction> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestPingInteraction(client, model.Id); | |||
await entity.UpdateAsync(client, model); | |||
return entity; | |||
} | |||
public string AcknowledgePing() | |||
{ | |||
var model = new API.InteractionResponse() | |||
{ | |||
Type = InteractionResponseType.Pong | |||
}; | |||
return SerializePayload(model); | |||
} | |||
public override string Defer(bool ephemeral = false, RequestOptions options = null) => throw new NotSupportedException(); | |||
public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) => throw new NotSupportedException(); | |||
} | |||
} |
@@ -0,0 +1,134 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.Interaction; | |||
using DataModel = Discord.API.AutocompleteInteractionData; | |||
using System.IO; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based autocomplete interaction. | |||
/// </summary> | |||
public class RestAutocompleteInteraction : RestInteraction, IDiscordInteraction | |||
{ | |||
/// <summary> | |||
/// Gets the autocomplete data of this interaction. | |||
/// </summary> | |||
public new RestAutocompleteInteractionData Data { get; } | |||
internal override bool _hasResponded { get; set; } | |||
private object _lock = new object(); | |||
internal RestAutocompleteInteraction(DiscordRestClient client, Model model) | |||
: base(client, model.Id) | |||
{ | |||
var dataModel = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
if (dataModel != null) | |||
Data = new RestAutocompleteInteractionData(dataModel); | |||
} | |||
internal new static async Task<RestAutocompleteInteraction> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestAutocompleteInteraction(client, model); | |||
await entity.UpdateAsync(client, model).ConfigureAwait(false); | |||
return entity; | |||
} | |||
/// <summary> | |||
/// Responds to this interaction with a set of choices. | |||
/// </summary> | |||
/// <param name="result"> | |||
/// The set of choices for the user to pick from. | |||
/// <remarks> | |||
/// A max of 20 choices are allowed. Passing <see langword="null"/> for this argument will show the executing user that | |||
/// there is no choices for their autocompleted input. | |||
/// </remarks> | |||
/// </param> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
public string Respond(IEnumerable<AutocompleteResult> result, RequestOptions options = null) | |||
{ | |||
if (!InteractionHelper.CanSendResponse(this)) | |||
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!"); | |||
lock (_lock) | |||
{ | |||
if (_hasResponded) | |||
{ | |||
throw new InvalidOperationException("Cannot respond twice to the same interaction"); | |||
} | |||
} | |||
lock (_lock) | |||
{ | |||
_hasResponded = true; | |||
} | |||
var model = new API.InteractionResponse | |||
{ | |||
Type = InteractionResponseType.ApplicationCommandAutocompleteResult, | |||
Data = new API.InteractionCallbackData | |||
{ | |||
Choices = result.Any() | |||
? result.Select(x => new API.ApplicationCommandOptionChoice { Name = x.Name, Value = x.Value }).ToArray() | |||
: Array.Empty<API.ApplicationCommandOptionChoice>() | |||
} | |||
}; | |||
return SerializePayload(model); | |||
} | |||
/// <summary> | |||
/// Responds to this interaction with a set of choices. | |||
/// </summary> | |||
/// <param name="options">The request options for this response.</param> | |||
/// <param name="result"> | |||
/// The set of choices for the user to pick from. | |||
/// <remarks> | |||
/// A max of 20 choices are allowed. Passing <see langword="null"/> for this argument will show the executing user that | |||
/// there is no choices for their autocompleted input. | |||
/// </remarks> | |||
/// </param> | |||
/// <returns> | |||
/// A string that contains json to write back to the incoming http request. | |||
/// </returns> | |||
public string Respond(RequestOptions options = null, params AutocompleteResult[] result) | |||
=> Respond(result, options); | |||
/// <inheritdoc/> | |||
[Obsolete("Autocomplete interactions cannot be deferred!", true)] | |||
public override string Defer(bool ephemeral = false, RequestOptions options = null) | |||
=> throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
/// <inheritdoc/> | |||
[Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
=> throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
/// <inheritdoc/> | |||
[Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
=> throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
/// <inheritdoc/> | |||
[Obsolete("Autocomplete interactions cannot have followups!", true)] | |||
public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string text = null, string fileName = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
=> throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
/// <inheritdoc/> | |||
[Obsolete("Autocomplete interactions cannot have normal responses!", true)] | |||
public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent component = null, Embed embed = null) | |||
=> throw new NotSupportedException("Autocomplete interactions cannot be deferred!"); | |||
IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.Immutable; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using DataModel = Discord.API.AutocompleteInteractionData; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents the data for a <see cref="RestAutocompleteInteraction"/>. | |||
/// </summary> | |||
public class RestAutocompleteInteractionData : IDiscordInteractionData | |||
{ | |||
/// <summary> | |||
/// Gets the name of the invoked command. | |||
/// </summary> | |||
public string CommandName { get; } | |||
/// <summary> | |||
/// Gets the id of the invoked command. | |||
/// </summary> | |||
public ulong CommandId { get; } | |||
/// <summary> | |||
/// Gets the type of the invoked command. | |||
/// </summary> | |||
public ApplicationCommandType Type { get; } | |||
/// <summary> | |||
/// Gets the version of the invoked command. | |||
/// </summary> | |||
public ulong Version { get; } | |||
/// <summary> | |||
/// Gets the current autocomplete option that is actively being filled out. | |||
/// </summary> | |||
public AutocompleteOption Current { get; } | |||
/// <summary> | |||
/// Gets a collection of all the other options the executing users has filled out. | |||
/// </summary> | |||
public IReadOnlyCollection<AutocompleteOption> Options { get; } | |||
internal RestAutocompleteInteractionData(DataModel model) | |||
{ | |||
var options = model.Options.SelectMany(GetOptions); | |||
Current = options.FirstOrDefault(x => x.Focused); | |||
Options = options.ToImmutableArray(); | |||
if (Options.Count == 1 && Current == null) | |||
Current = Options.FirstOrDefault(); | |||
CommandName = model.Name; | |||
CommandId = model.Id; | |||
Type = model.Type; | |||
Version = model.Version; | |||
} | |||
private List<AutocompleteOption> GetOptions(API.AutocompleteInteractionDataOption model) | |||
{ | |||
var options = new List<AutocompleteOption>(); | |||
options.Add(new AutocompleteOption(model.Type, model.Name, model.Value.GetValueOrDefault(null), model.Focused.GetValueOrDefault(false))); | |||
if (model.Options.IsSpecified) | |||
{ | |||
options.AddRange(model.Options.Value.SelectMany(GetOptions)); | |||
} | |||
return options; | |||
} | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using DataModel = Discord.API.ApplicationCommandInteractionData; | |||
using Model = Discord.API.Interaction; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based slash command. | |||
/// </summary> | |||
public class RestSlashCommand : RestCommandBase, IDiscordInteraction | |||
{ | |||
/// <summary> | |||
/// Gets the data associated with this interaction. | |||
/// </summary> | |||
public new RestSlashCommandData Data { get; private set; } | |||
internal RestSlashCommand(DiscordRestClient client, Model model) | |||
: base(client, model) | |||
{ | |||
} | |||
internal new static async Task<RestSlashCommand> CreateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var entity = new RestSlashCommand(client, model); | |||
await entity.UpdateAsync(client, model); | |||
return entity; | |||
} | |||
internal override async Task UpdateAsync(DiscordRestClient client, Model model) | |||
{ | |||
var dataModel = model.Data.IsSpecified | |||
? (DataModel)model.Data.Value | |||
: null; | |||
Data = await RestSlashCommandData.CreateAsync(client, dataModel, Guild, Channel); | |||
} | |||
IDiscordInteractionData IDiscordInteraction.Data => Data; | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.Immutable; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.ApplicationCommandInteractionData; | |||
namespace Discord.Rest | |||
{ | |||
public class RestSlashCommandData : RestCommandBaseData<RestSlashCommandDataOption>, IDiscordInteractionData | |||
{ | |||
internal RestSlashCommandData(DiscordRestClient client, Model model) | |||
: base(client, model) { } | |||
internal static new async Task<RestSlashCommandData> CreateAsync(DiscordRestClient client, Model model, IGuild guild, IRestMessageChannel channel) | |||
{ | |||
var entity = new RestSlashCommandData(client, model); | |||
await entity.UpdateAsync(client, model, guild, channel); | |||
return entity; | |||
} | |||
internal override async Task UpdateAsync(DiscordRestClient client, Model model, IGuild guild, IRestMessageChannel channel) | |||
{ | |||
await base.UpdateAsync(client, model, guild, channel); | |||
Options = model.Options.IsSpecified | |||
? model.Options.Value.Select(x => new RestSlashCommandDataOption(this, x)).ToImmutableArray() | |||
: ImmutableArray.Create<RestSlashCommandDataOption>(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,139 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.Immutable; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.ApplicationCommandInteractionDataOption; | |||
namespace Discord.Rest | |||
{ | |||
/// <summary> | |||
/// Represents a REST-based option for a slash command. | |||
/// </summary> | |||
public class RestSlashCommandDataOption : IApplicationCommandInteractionDataOption | |||
{ | |||
#region RestSlashCommandDataOption | |||
/// <inheritdoc/> | |||
public string Name { get; private set; } | |||
/// <inheritdoc/> | |||
public object Value { get; private set; } | |||
/// <inheritdoc/> | |||
public ApplicationCommandOptionType Type { get; private set; } | |||
/// <summary> | |||
/// Gets a collection of sub command options received for this sub command group. | |||
/// </summary> | |||
public IReadOnlyCollection<RestSlashCommandDataOption> Options { get; private set; } | |||
internal RestSlashCommandDataOption() { } | |||
internal RestSlashCommandDataOption(RestSlashCommandData data, Model model) | |||
{ | |||
Name = model.Name; | |||
Type = model.Type; | |||
if (model.Value.IsSpecified) | |||
{ | |||
switch (Type) | |||
{ | |||
case ApplicationCommandOptionType.User: | |||
case ApplicationCommandOptionType.Role: | |||
case ApplicationCommandOptionType.Channel: | |||
case ApplicationCommandOptionType.Mentionable: | |||
if (ulong.TryParse($"{model.Value.Value}", out var valueId)) | |||
{ | |||
switch (Type) | |||
{ | |||
case ApplicationCommandOptionType.User: | |||
{ | |||
var guildUser = data.ResolvableData.GuildMembers.FirstOrDefault(x => x.Key == valueId).Value; | |||
if (guildUser != null) | |||
Value = guildUser; | |||
else | |||
Value = data.ResolvableData.Users.FirstOrDefault(x => x.Key == valueId).Value; | |||
} | |||
break; | |||
case ApplicationCommandOptionType.Channel: | |||
Value = data.ResolvableData.Channels.FirstOrDefault(x => x.Key == valueId).Value; | |||
break; | |||
case ApplicationCommandOptionType.Role: | |||
Value = data.ResolvableData.Roles.FirstOrDefault(x => x.Key == valueId).Value; | |||
break; | |||
case ApplicationCommandOptionType.Mentionable: | |||
{ | |||
if (data.ResolvableData.GuildMembers.Any(x => x.Key == valueId) || data.ResolvableData.Users.Any(x => x.Key == valueId)) | |||
{ | |||
var guildUser = data.ResolvableData.GuildMembers.FirstOrDefault(x => x.Key == valueId).Value; | |||
if (guildUser != null) | |||
Value = guildUser; | |||
else | |||
Value = data.ResolvableData.Users.FirstOrDefault(x => x.Key == valueId).Value; | |||
} | |||
else if (data.ResolvableData.Roles.Any(x => x.Key == valueId)) | |||
{ | |||
Value = data.ResolvableData.Roles.FirstOrDefault(x => x.Key == valueId).Value; | |||
} | |||
} | |||
break; | |||
default: | |||
Value = model.Value.Value; | |||
break; | |||
} | |||
} | |||
break; | |||
case ApplicationCommandOptionType.String: | |||
Value = model.Value.ToString(); | |||
break; | |||
case ApplicationCommandOptionType.Integer: | |||
{ | |||
if (model.Value.Value is long val) | |||
Value = val; | |||
else if (long.TryParse(model.Value.Value.ToString(), out long res)) | |||
Value = res; | |||
} | |||
break; | |||
case ApplicationCommandOptionType.Boolean: | |||
{ | |||
if (model.Value.Value is bool val) | |||
Value = val; | |||
else if (bool.TryParse(model.Value.Value.ToString(), out bool res)) | |||
Value = res; | |||
} | |||
break; | |||
case ApplicationCommandOptionType.Number: | |||
{ | |||
if (model.Value.Value is int val) | |||
Value = val; | |||
else if (double.TryParse(model.Value.Value.ToString(), out double res)) | |||
Value = res; | |||
} | |||
break; | |||
} | |||
} | |||
Options = model.Options.IsSpecified | |||
? model.Options.Value.Select(x => new RestSlashCommandDataOption(data, x)).ToImmutableArray() | |||
: ImmutableArray.Create<RestSlashCommandDataOption>(); | |||
} | |||
#endregion | |||
#region Converters | |||
public static explicit operator bool(RestSlashCommandDataOption option) | |||
=> (bool)option.Value; | |||
public static explicit operator int(RestSlashCommandDataOption option) | |||
=> (int)option.Value; | |||
public static explicit operator string(RestSlashCommandDataOption option) | |||
=> option.Value.ToString(); | |||
#endregion | |||
#region IApplicationCommandInteractionDataOption | |||
IReadOnlyCollection<IApplicationCommandInteractionDataOption> IApplicationCommandInteractionDataOption.Options | |||
=> Options; | |||
#endregion | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.Rest | |||
{ | |||
public class BadSignatureException : Exception | |||
{ | |||
internal BadSignatureException() : base("Failed to verify authenticity of message: public key doesnt match signature") | |||
{ | |||
} | |||
} | |||
} |
@@ -22,7 +22,7 @@ namespace Discord.Net.Converters | |||
// Remove the data property for manual deserialization | |||
var result = obj.GetValue("data", StringComparison.OrdinalIgnoreCase); | |||
result.Parent.Remove(); | |||
result?.Parent.Remove(); | |||
// Populate the remaining properties. | |||
using (var subReader = obj.CreateReader()) | |||
@@ -0,0 +1,27 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
// Array16<UInt32> Salsa20 state | |||
// Array16<UInt64> SHA-512 block | |||
internal struct Array16<T> | |||
{ | |||
public T x0; | |||
public T x1; | |||
public T x2; | |||
public T x3; | |||
public T x4; | |||
public T x5; | |||
public T x6; | |||
public T x7; | |||
public T x8; | |||
public T x9; | |||
public T x10; | |||
public T x11; | |||
public T x12; | |||
public T x13; | |||
public T x14; | |||
public T x15; | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using System; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
// Array8<UInt32> Poly1305 key | |||
// Array8<UInt64> SHA-512 state/output | |||
internal struct Array8<T> | |||
{ | |||
public T x0; | |||
public T x1; | |||
public T x2; | |||
public T x3; | |||
public T x4; | |||
public T x5; | |||
public T x6; | |||
public T x7; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
using System; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
// Loops? Arrays? Never heard of that stuff | |||
// Library avoids unnecessary heap allocations and unsafe code | |||
// so this ugly code becomes necessary :( | |||
internal static class ByteIntegerConverter | |||
{ | |||
public static ulong LoadBigEndian64(byte[] buf, int offset) | |||
{ | |||
return | |||
(ulong)(buf[offset + 7]) | |||
| (((ulong)(buf[offset + 6])) << 8) | |||
| (((ulong)(buf[offset + 5])) << 16) | |||
| (((ulong)(buf[offset + 4])) << 24) | |||
| (((ulong)(buf[offset + 3])) << 32) | |||
| (((ulong)(buf[offset + 2])) << 40) | |||
| (((ulong)(buf[offset + 1])) << 48) | |||
| (((ulong)(buf[offset + 0])) << 56); | |||
} | |||
public static void StoreBigEndian64(byte[] buf, int offset, ulong value) | |||
{ | |||
buf[offset + 7] = unchecked((byte)value); | |||
buf[offset + 6] = unchecked((byte)(value >> 8)); | |||
buf[offset + 5] = unchecked((byte)(value >> 16)); | |||
buf[offset + 4] = unchecked((byte)(value >> 24)); | |||
buf[offset + 3] = unchecked((byte)(value >> 32)); | |||
buf[offset + 2] = unchecked((byte)(value >> 40)); | |||
buf[offset + 1] = unchecked((byte)(value >> 48)); | |||
buf[offset + 0] = unchecked((byte)(value >> 56)); | |||
} | |||
public static void Array16LoadBigEndian64(out Array16<ulong> output, byte[] input, int inputOffset) | |||
{ | |||
output.x0 = LoadBigEndian64(input, inputOffset + 0); | |||
output.x1 = LoadBigEndian64(input, inputOffset + 8); | |||
output.x2 = LoadBigEndian64(input, inputOffset + 16); | |||
output.x3 = LoadBigEndian64(input, inputOffset + 24); | |||
output.x4 = LoadBigEndian64(input, inputOffset + 32); | |||
output.x5 = LoadBigEndian64(input, inputOffset + 40); | |||
output.x6 = LoadBigEndian64(input, inputOffset + 48); | |||
output.x7 = LoadBigEndian64(input, inputOffset + 56); | |||
output.x8 = LoadBigEndian64(input, inputOffset + 64); | |||
output.x9 = LoadBigEndian64(input, inputOffset + 72); | |||
output.x10 = LoadBigEndian64(input, inputOffset + 80); | |||
output.x11 = LoadBigEndian64(input, inputOffset + 88); | |||
output.x12 = LoadBigEndian64(input, inputOffset + 96); | |||
output.x13 = LoadBigEndian64(input, inputOffset + 104); | |||
output.x14 = LoadBigEndian64(input, inputOffset + 112); | |||
output.x15 = LoadBigEndian64(input, inputOffset + 120); | |||
} | |||
} | |||
} |
@@ -0,0 +1,272 @@ | |||
using System; | |||
using System.Linq; | |||
using System.Numerics; | |||
using System.Runtime.CompilerServices; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
internal class CryptoBytes | |||
{ | |||
/// <summary> | |||
/// Comparison of two arrays. | |||
/// | |||
/// The runtime of this method does not depend on the contents of the arrays. Using constant time | |||
/// prevents timing attacks that allow an attacker to learn if the arrays have a common prefix. | |||
/// | |||
/// It is important to use such a constant time comparison when verifying MACs. | |||
/// </summary> | |||
/// <param name="x">Byte array</param> | |||
/// <param name="y">Byte array</param> | |||
/// <returns>True if arrays are equal</returns> | |||
public static bool ConstantTimeEquals(byte[] x, byte[] y) | |||
{ | |||
if (x.Length != y.Length) | |||
return false; | |||
return InternalConstantTimeEquals(x, 0, y, 0, x.Length) != 0; | |||
} | |||
/// <summary> | |||
/// Comparison of two array segments. | |||
/// | |||
/// The runtime of this method does not depend on the contents of the arrays. Using constant time | |||
/// prevents timing attacks that allow an attacker to learn if the arrays have a common prefix. | |||
/// | |||
/// It is important to use such a constant time comparison when verifying MACs. | |||
/// </summary> | |||
/// <param name="x">Byte array segment</param> | |||
/// <param name="y">Byte array segment</param> | |||
/// <returns>True if contents of x and y are equal</returns> | |||
public static bool ConstantTimeEquals(ArraySegment<byte> x, ArraySegment<byte> y) | |||
{ | |||
if (x.Count != y.Count) | |||
return false; | |||
return InternalConstantTimeEquals(x.Array, x.Offset, y.Array, y.Offset, x.Count) != 0; | |||
} | |||
/// <summary> | |||
/// Comparison of two byte sequences. | |||
/// | |||
/// The runtime of this method does not depend on the contents of the arrays. Using constant time | |||
/// prevents timing attacks that allow an attacker to learn if the arrays have a common prefix. | |||
/// | |||
/// It is important to use such a constant time comparison when verifying MACs. | |||
/// </summary> | |||
/// <param name="x">Byte array</param> | |||
/// <param name="xOffset">Offset of byte sequence in the x array</param> | |||
/// <param name="y">Byte array</param> | |||
/// <param name="yOffset">Offset of byte sequence in the y array</param> | |||
/// <param name="length">Lengh of byte sequence</param> | |||
/// <returns>True if sequences are equal</returns> | |||
public static bool ConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length) | |||
{ | |||
return InternalConstantTimeEquals(x, xOffset, y, yOffset, length) != 0; | |||
} | |||
private static uint InternalConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length) | |||
{ | |||
int differentbits = 0; | |||
for (int i = 0; i < length; i++) | |||
differentbits |= x[xOffset + i] ^ y[yOffset + i]; | |||
return (1 & (unchecked((uint)differentbits - 1) >> 8)); | |||
} | |||
/// <summary> | |||
/// Overwrites the contents of the array, wiping the previous content. | |||
/// </summary> | |||
/// <param name="data">Byte array</param> | |||
public static void Wipe(byte[] data) | |||
{ | |||
InternalWipe(data, 0, data.Length); | |||
} | |||
/// <summary> | |||
/// Overwrites the contents of the array, wiping the previous content. | |||
/// </summary> | |||
/// <param name="data">Byte array</param> | |||
/// <param name="offset">Index of byte sequence</param> | |||
/// <param name="length">Length of byte sequence</param> | |||
public static void Wipe(byte[] data, int offset, int length) | |||
{ | |||
InternalWipe(data, offset, length); | |||
} | |||
/// <summary> | |||
/// Overwrites the contents of the array segment, wiping the previous content. | |||
/// </summary> | |||
/// <param name="data">Byte array segment</param> | |||
public static void Wipe(ArraySegment<byte> data) | |||
{ | |||
InternalWipe(data.Array, data.Offset, data.Count); | |||
} | |||
// Secure wiping is hard | |||
// * the GC can move around and copy memory | |||
// Perhaps this can be avoided by using unmanaged memory or by fixing the position of the array in memory | |||
// * Swap files and error dumps can contain secret information | |||
// It seems possible to lock memory in RAM, no idea about error dumps | |||
// * Compiler could optimize out the wiping if it knows that data won't be read back | |||
// I hope this is enough, suppressing inlining | |||
// but perhaps `RtlSecureZeroMemory` is needed | |||
[MethodImpl(MethodImplOptions.NoInlining)] | |||
internal static void InternalWipe(byte[] data, int offset, int count) | |||
{ | |||
Array.Clear(data, offset, count); | |||
} | |||
// shallow wipe of structs | |||
[MethodImpl(MethodImplOptions.NoInlining)] | |||
internal static void InternalWipe<T>(ref T data) | |||
where T : struct | |||
{ | |||
data = default(T); | |||
} | |||
/// <summary> | |||
/// Constant-time conversion of the bytes array to an upper-case hex string. | |||
/// Please see http://stackoverflow.com/a/14333437/445517 for the detailed explanation | |||
/// </summary> | |||
/// <param name="data">Byte array</param> | |||
/// <returns>Hex representation of byte array</returns> | |||
public static string ToHexStringUpper(byte[] data) | |||
{ | |||
if (data == null) | |||
return null; | |||
char[] c = new char[data.Length * 2]; | |||
int b; | |||
for (int i = 0; i < data.Length; i++) | |||
{ | |||
b = data[i] >> 4; | |||
c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); | |||
b = data[i] & 0xF; | |||
c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); | |||
} | |||
return new string(c); | |||
} | |||
/// <summary> | |||
/// Constant-time conversion of the bytes array to an lower-case hex string. | |||
/// Please see http://stackoverflow.com/a/14333437/445517 for the detailed explanation. | |||
/// </summary> | |||
/// <param name="data">Byte array</param> | |||
/// <returns>Hex representation of byte array</returns> | |||
public static string ToHexStringLower(byte[] data) | |||
{ | |||
if (data == null) | |||
return null; | |||
char[] c = new char[data.Length * 2]; | |||
int b; | |||
for (int i = 0; i < data.Length; i++) | |||
{ | |||
b = data[i] >> 4; | |||
c[i * 2] = (char)(87 + b + (((b - 10) >> 31) & -39)); | |||
b = data[i] & 0xF; | |||
c[i * 2 + 1] = (char)(87 + b + (((b - 10) >> 31) & -39)); | |||
} | |||
return new string(c); | |||
} | |||
/// <summary> | |||
/// Converts the hex string to bytes. Case insensitive. | |||
/// </summary> | |||
/// <param name="hexString">Hex encoded byte sequence</param> | |||
/// <returns>Byte array</returns> | |||
public static byte[] FromHexString(string hexString) | |||
{ | |||
if (hexString == null) | |||
return null; | |||
if (hexString.Length % 2 != 0) | |||
throw new FormatException("The hex string is invalid because it has an odd length"); | |||
var result = new byte[hexString.Length / 2]; | |||
for (int i = 0; i < result.Length; i++) | |||
result[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); | |||
return result; | |||
} | |||
/// <summary> | |||
/// Encodes the bytes with the Base64 encoding. | |||
/// More compact than hex, but it is case-sensitive and uses the special characters `+`, `/` and `=`. | |||
/// </summary> | |||
/// <param name="data">Byte array</param> | |||
/// <returns>Base 64 encoded data</returns> | |||
public static string ToBase64String(byte[] data) | |||
{ | |||
if (data == null) | |||
return null; | |||
return Convert.ToBase64String(data); | |||
} | |||
/// <summary> | |||
/// Decodes a Base64 encoded string back to bytes. | |||
/// </summary> | |||
/// <param name="base64String">Base 64 encoded data</param> | |||
/// <returns>Byte array</returns> | |||
public static byte[] FromBase64String(string base64String) | |||
{ | |||
if (base64String == null) | |||
return null; | |||
return Convert.FromBase64String(base64String); | |||
} | |||
private const string strDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; | |||
/// <summary> | |||
/// Encode a byte sequence as a base58-encoded string | |||
/// </summary> | |||
/// <param name="input">Byte sequence</param> | |||
/// <returns>Encoding result</returns> | |||
public static string Base58Encode(byte[] input) | |||
{ | |||
// Decode byte[] to BigInteger | |||
BigInteger intData = 0; | |||
for (int i = 0; i < input.Length; i++) | |||
{ | |||
intData = intData * 256 + input[i]; | |||
} | |||
// Encode BigInteger to Base58 string | |||
string result = ""; | |||
while (intData > 0) | |||
{ | |||
int remainder = (int)(intData % 58); | |||
intData /= 58; | |||
result = strDigits[remainder] + result; | |||
} | |||
// Append `1` for each leading 0 byte | |||
for (int i = 0; i < input.Length && input[i] == 0; i++) | |||
{ | |||
result = '1' + result; | |||
} | |||
return result; | |||
} | |||
/// <summary> | |||
/// // Decode a base58-encoded string into byte array | |||
/// </summary> | |||
/// <param name="strBase58">Base58 data string</param> | |||
/// <returns>Byte array</returns> | |||
public static byte[] Base58Decode(string input) | |||
{ | |||
// Decode Base58 string to BigInteger | |||
BigInteger intData = 0; | |||
for (int i = 0; i < input.Length; i++) | |||
{ | |||
int digit = strDigits.IndexOf(input[i]); //Slow | |||
if (digit < 0) | |||
throw new FormatException(string.Format("Invalid Base58 character `{0}` at position {1}", input[i], i)); | |||
intData = intData * 58 + digit; | |||
} | |||
// Encode BigInteger to byte[] | |||
// Leading zero bytes get encoded as leading `1` characters | |||
int leadingZeroCount = input.TakeWhile(c => c == '1').Count(); | |||
var leadingZeros = Enumerable.Repeat((byte)0, leadingZeroCount); | |||
var bytesWithoutLeadingZeros = | |||
intData.ToByteArray() | |||
.Reverse()// to big endian | |||
.SkipWhile(b => b == 0);//strip sign byte | |||
var result = leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); | |||
return result; | |||
} | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
internal static class Ed25519 | |||
{ | |||
/// <summary> | |||
/// Public Keys are 32 byte values. All possible values of this size a valid. | |||
/// </summary> | |||
public const int PublicKeySize = 32; | |||
/// <summary> | |||
/// Signatures are 64 byte values | |||
/// </summary> | |||
public const int SignatureSize = 64; | |||
/// <summary> | |||
/// Private key seeds are 32 byte arbitrary values. This is the form that should be generated and stored. | |||
/// </summary> | |||
public const int PrivateKeySeedSize = 32; | |||
/// <summary> | |||
/// A 64 byte expanded form of private key. This form is used internally to improve performance | |||
/// </summary> | |||
public const int ExpandedPrivateKeySize = 32 * 2; | |||
/// <summary> | |||
/// Verify Ed25519 signature | |||
/// </summary> | |||
/// <param name="signature">Signature bytes</param> | |||
/// <param name="message">Message</param> | |||
/// <param name="publicKey">Public key</param> | |||
/// <returns>True if signature is valid, false if it's not</returns> | |||
public static bool Verify(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> publicKey) | |||
{ | |||
if (signature.Count != SignatureSize) | |||
throw new ArgumentException($"Sizeof signature doesnt match defined size of {SignatureSize}"); | |||
if (publicKey.Count != PublicKeySize) | |||
throw new ArgumentException($"Sizeof public key doesnt match defined size of {PublicKeySize}"); | |||
return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset); | |||
} | |||
/// <summary> | |||
/// Verify Ed25519 signature | |||
/// </summary> | |||
/// <param name="signature">Signature bytes</param> | |||
/// <param name="message">Message</param> | |||
/// <param name="publicKey">Public key</param> | |||
/// <returns>True if signature is valid, false if it's not</returns> | |||
public static bool Verify(byte[] signature, byte[] message, byte[] publicKey) | |||
{ | |||
Preconditions.NotNull(signature, nameof(signature)); | |||
Preconditions.NotNull(message, nameof(message)); | |||
Preconditions.NotNull(publicKey, nameof(publicKey)); | |||
if (signature.Length != SignatureSize) | |||
throw new ArgumentException($"Sizeof signature doesnt match defined size of {SignatureSize}"); | |||
if (publicKey.Length != PublicKeySize) | |||
throw new ArgumentException($"Sizeof public key doesnt match defined size of {PublicKeySize}"); | |||
return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0); | |||
} | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
using Discord.Net.ED25519.Ed25519Ref10; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
internal class Ed25519Operations | |||
{ | |||
public static bool crypto_sign_verify( | |||
byte[] sig, int sigoffset, | |||
byte[] m, int moffset, int mlen, | |||
byte[] pk, int pkoffset) | |||
{ | |||
byte[] h; | |||
byte[] checkr = new byte[32]; | |||
GroupElementP3 A; | |||
GroupElementP2 R; | |||
if ((sig[sigoffset + 63] & 224) != 0) | |||
return false; | |||
if (GroupOperations.ge_frombytes_negate_vartime(out A, pk, pkoffset) != 0) | |||
return false; | |||
var hasher = new Sha512(); | |||
hasher.Update(sig, sigoffset, 32); | |||
hasher.Update(pk, pkoffset, 32); | |||
hasher.Update(m, moffset, mlen); | |||
h = hasher.Finalize(); | |||
ScalarOperations.sc_reduce(h); | |||
var sm32 = new byte[32]; | |||
Array.Copy(sig, sigoffset + 32, sm32, 0, 32); | |||
GroupOperations.ge_double_scalarmult_vartime(out R, h, ref A, sm32); | |||
GroupOperations.ge_tobytes(checkr, 0, ref R); | |||
var result = CryptoBytes.ConstantTimeEquals(checkr, 0, sig, sigoffset, 32); | |||
CryptoBytes.Wipe(h); | |||
CryptoBytes.Wipe(checkr); | |||
return result; | |||
} | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal struct FieldElement | |||
{ | |||
internal int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9; | |||
internal FieldElement(params int[] elements) | |||
{ | |||
x0 = elements[0]; | |||
x1 = elements[1]; | |||
x2 = elements[2]; | |||
x3 = elements[3]; | |||
x4 = elements[4]; | |||
x5 = elements[5]; | |||
x6 = elements[6]; | |||
x7 = elements[7]; | |||
x8 = elements[8]; | |||
x9 = elements[9]; | |||
} | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
/* | |||
ge means group element. | |||
Here the group is the set of pairs (x,y) of field elements (see fe.h) | |||
satisfying -x^2 + y^2 = 1 + d x^2y^2 | |||
where d = -121665/121666. | |||
Representations: | |||
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z | |||
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT | |||
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T | |||
ge_precomp (Duif): (y+x,y-x,2dxy) | |||
*/ | |||
internal struct GroupElementP2 | |||
{ | |||
public FieldElement X; | |||
public FieldElement Y; | |||
public FieldElement Z; | |||
} ; | |||
internal struct GroupElementP3 | |||
{ | |||
public FieldElement X; | |||
public FieldElement Y; | |||
public FieldElement Z; | |||
public FieldElement T; | |||
} ; | |||
internal struct GroupElementP1P1 | |||
{ | |||
public FieldElement X; | |||
public FieldElement Y; | |||
public FieldElement Z; | |||
public FieldElement T; | |||
} ; | |||
internal struct GroupElementPreComp | |||
{ | |||
public FieldElement yplusx; | |||
public FieldElement yminusx; | |||
public FieldElement xy2d; | |||
public GroupElementPreComp(FieldElement yplusx, FieldElement yminusx, FieldElement xy2d) | |||
{ | |||
this.yplusx = yplusx; | |||
this.yminusx = yminusx; | |||
this.xy2d = xy2d; | |||
} | |||
} ; | |||
internal struct GroupElementCached | |||
{ | |||
public FieldElement YplusX; | |||
public FieldElement YminusX; | |||
public FieldElement Z; | |||
public FieldElement T2d; | |||
} ; | |||
} |
@@ -0,0 +1,50 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class LookupTables | |||
{ | |||
internal static readonly GroupElementPreComp[] Base2 = new GroupElementPreComp[]{ | |||
new GroupElementPreComp( | |||
new FieldElement( 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 ), | |||
new FieldElement( -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 ), | |||
new FieldElement( -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 ), | |||
new FieldElement( 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 ), | |||
new FieldElement( 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 ), | |||
new FieldElement( 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 ), | |||
new FieldElement( 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 ), | |||
new FieldElement( -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 ), | |||
new FieldElement( 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 ), | |||
new FieldElement( -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 ), | |||
new FieldElement( 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 ), | |||
new FieldElement( 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 ), | |||
new FieldElement( 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 ), | |||
new FieldElement( -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 ), | |||
new FieldElement( -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 ) | |||
), | |||
new GroupElementPreComp( | |||
new FieldElement( -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 ), | |||
new FieldElement( -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 ), | |||
new FieldElement( -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 ) | |||
) | |||
}; | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class LookupTables | |||
{ | |||
internal static FieldElement d = new FieldElement(-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116); | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class LookupTables | |||
{ | |||
internal static FieldElement d2 = new FieldElement(-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
public static void fe_0(out FieldElement h) | |||
{ | |||
h = default(FieldElement); | |||
} | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
public static void fe_1(out FieldElement h) | |||
{ | |||
h = default(FieldElement); | |||
h.x0 = 1; | |||
} | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = f + g | |||
Can overlap h with f or g. | |||
Preconditions: | |||
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
Postconditions: | |||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | |||
*/ | |||
//void fe_add(fe h,const fe f,const fe g) | |||
internal static void fe_add(out FieldElement h, ref FieldElement f, ref FieldElement g) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int g0 = g.x0; | |||
int g1 = g.x1; | |||
int g2 = g.x2; | |||
int g3 = g.x3; | |||
int g4 = g.x4; | |||
int g5 = g.x5; | |||
int g6 = g.x6; | |||
int g7 = g.x7; | |||
int g8 = g.x8; | |||
int g9 = g.x9; | |||
int h0 = f0 + g0; | |||
int h1 = f1 + g1; | |||
int h2 = f2 + g2; | |||
int h3 = f3 + g3; | |||
int h4 = f4 + g4; | |||
int h5 = f5 + g5; | |||
int h6 = f6 + g6; | |||
int h7 = f7 + g7; | |||
int h8 = f8 + g8; | |||
int h9 = f9 + g9; | |||
h.x0 = h0; | |||
h.x1 = h1; | |||
h.x2 = h2; | |||
h.x3 = h3; | |||
h.x4 = h4; | |||
h.x5 = h5; | |||
h.x6 = h6; | |||
h.x7 = h7; | |||
h.x8 = h8; | |||
h.x9 = h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
Replace (f,g) with (g,g) if b == 1; | |||
replace (f,g) with (f,g) if b == 0. | |||
Preconditions: b in {0,1}. | |||
*/ | |||
//void fe_cmov(fe f,const fe g,unsigned int b) | |||
internal static void fe_cmov(ref FieldElement f, ref FieldElement g, int b) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int g0 = g.x0; | |||
int g1 = g.x1; | |||
int g2 = g.x2; | |||
int g3 = g.x3; | |||
int g4 = g.x4; | |||
int g5 = g.x5; | |||
int g6 = g.x6; | |||
int g7 = g.x7; | |||
int g8 = g.x8; | |||
int g9 = g.x9; | |||
int x0 = f0 ^ g0; | |||
int x1 = f1 ^ g1; | |||
int x2 = f2 ^ g2; | |||
int x3 = f3 ^ g3; | |||
int x4 = f4 ^ g4; | |||
int x5 = f5 ^ g5; | |||
int x6 = f6 ^ g6; | |||
int x7 = f7 ^ g7; | |||
int x8 = f8 ^ g8; | |||
int x9 = f9 ^ g9; | |||
b = -b; | |||
x0 &= b; | |||
x1 &= b; | |||
x2 &= b; | |||
x3 &= b; | |||
x4 &= b; | |||
x5 &= b; | |||
x6 &= b; | |||
x7 &= b; | |||
x8 &= b; | |||
x9 &= b; | |||
f.x0 = f0 ^ x0; | |||
f.x1 = f1 ^ x1; | |||
f.x2 = f2 ^ x2; | |||
f.x3 = f3 ^ x3; | |||
f.x4 = f4 ^ x4; | |||
f.x5 = f5 ^ x5; | |||
f.x6 = f6 ^ x6; | |||
f.x7 = f7 ^ x7; | |||
f.x8 = f8 ^ x8; | |||
f.x9 = f9 ^ x9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,79 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
Replace (f,g) with (g,f) if b == 1; | |||
replace (f,g) with (f,g) if b == 0. | |||
Preconditions: b in {0,1}. | |||
*/ | |||
public static void fe_cswap(ref FieldElement f, ref FieldElement g, uint b) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int g0 = g.x0; | |||
int g1 = g.x1; | |||
int g2 = g.x2; | |||
int g3 = g.x3; | |||
int g4 = g.x4; | |||
int g5 = g.x5; | |||
int g6 = g.x6; | |||
int g7 = g.x7; | |||
int g8 = g.x8; | |||
int g9 = g.x9; | |||
int x0 = f0 ^ g0; | |||
int x1 = f1 ^ g1; | |||
int x2 = f2 ^ g2; | |||
int x3 = f3 ^ g3; | |||
int x4 = f4 ^ g4; | |||
int x5 = f5 ^ g5; | |||
int x6 = f6 ^ g6; | |||
int x7 = f7 ^ g7; | |||
int x8 = f8 ^ g8; | |||
int x9 = f9 ^ g9; | |||
int negb = unchecked((int)-b); | |||
x0 &= negb; | |||
x1 &= negb; | |||
x2 &= negb; | |||
x3 &= negb; | |||
x4 &= negb; | |||
x5 &= negb; | |||
x6 &= negb; | |||
x7 &= negb; | |||
x8 &= negb; | |||
x9 &= negb; | |||
f.x0 = f0 ^ x0; | |||
f.x1 = f1 ^ x1; | |||
f.x2 = f2 ^ x2; | |||
f.x3 = f3 ^ x3; | |||
f.x4 = f4 ^ x4; | |||
f.x5 = f5 ^ x5; | |||
f.x6 = f6 ^ x6; | |||
f.x7 = f7 ^ x7; | |||
f.x8 = f8 ^ x8; | |||
f.x9 = f9 ^ x9; | |||
g.x0 = g0 ^ x0; | |||
g.x1 = g1 ^ x1; | |||
g.x2 = g2 ^ x2; | |||
g.x3 = g3 ^ x3; | |||
g.x4 = g4 ^ x4; | |||
g.x5 = g5 ^ x5; | |||
g.x6 = g6 ^ x6; | |||
g.x7 = g7 ^ x7; | |||
g.x8 = g8 ^ x8; | |||
g.x9 = g9 ^ x9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,102 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
private static long load_3(byte[] data, int offset) | |||
{ | |||
uint result; | |||
result = data[offset + 0]; | |||
result |= (uint)data[offset + 1] << 8; | |||
result |= (uint)data[offset + 2] << 16; | |||
return (long)(ulong)result; | |||
} | |||
private static long load_4(byte[] data, int offset) | |||
{ | |||
uint result; | |||
result = data[offset + 0]; | |||
result |= (uint)data[offset + 1] << 8; | |||
result |= (uint)data[offset + 2] << 16; | |||
result |= (uint)data[offset + 3] << 24; | |||
return (long)(ulong)result; | |||
} | |||
// Ignores top bit of h. | |||
internal static void fe_frombytes(out FieldElement h, byte[] data, int offset) | |||
{ | |||
var h0 = load_4(data, offset); | |||
var h1 = load_3(data, offset + 4) << 6; | |||
var h2 = load_3(data, offset + 7) << 5; | |||
var h3 = load_3(data, offset + 10) << 3; | |||
var h4 = load_3(data, offset + 13) << 2; | |||
var h5 = load_4(data, offset + 16); | |||
var h6 = load_3(data, offset + 20) << 7; | |||
var h7 = load_3(data, offset + 23) << 5; | |||
var h8 = load_3(data, offset + 26) << 4; | |||
var h9 = (load_3(data, offset + 29) & 8388607) << 2; | |||
var carry9 = (h9 + (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; | |||
var carry1 = (h1 + (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
var carry3 = (h3 + (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
var carry5 = (h5 + (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
var carry7 = (h7 + (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
var carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
var carry2 = (h2 + (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
var carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry6 = (h6 + (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
var carry8 = (h8 + (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
h.x0 = (int)h0; | |||
h.x1 = (int)h1; | |||
h.x2 = (int)h2; | |||
h.x3 = (int)h3; | |||
h.x4 = (int)h4; | |||
h.x5 = (int)h5; | |||
h.x6 = (int)h6; | |||
h.x7 = (int)h7; | |||
h.x8 = (int)h8; | |||
h.x9 = (int)h9; | |||
} | |||
// does NOT ignore top bit | |||
internal static void fe_frombytes2(out FieldElement h, byte[] data, int offset) | |||
{ | |||
var h0 = load_4(data, offset); | |||
var h1 = load_3(data, offset + 4) << 6; | |||
var h2 = load_3(data, offset + 7) << 5; | |||
var h3 = load_3(data, offset + 10) << 3; | |||
var h4 = load_3(data, offset + 13) << 2; | |||
var h5 = load_4(data, offset + 16); | |||
var h6 = load_3(data, offset + 20) << 7; | |||
var h7 = load_3(data, offset + 23) << 5; | |||
var h8 = load_3(data, offset + 26) << 4; | |||
var h9 = load_3(data, offset + 29) << 2; | |||
var carry9 = (h9 + (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; | |||
var carry1 = (h1 + (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
var carry3 = (h3 + (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
var carry5 = (h5 + (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
var carry7 = (h7 + (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
var carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
var carry2 = (h2 + (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
var carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry6 = (h6 + (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
var carry8 = (h8 + (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
h.x0 = (int)h0; | |||
h.x1 = (int)h1; | |||
h.x2 = (int)h2; | |||
h.x3 = (int)h3; | |||
h.x4 = (int)h4; | |||
h.x5 = (int)h5; | |||
h.x6 = (int)h6; | |||
h.x7 = (int)h7; | |||
h.x8 = (int)h8; | |||
h.x9 = (int)h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,128 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
internal static void fe_invert(out FieldElement result, ref FieldElement z) | |||
{ | |||
FieldElement t0, t1, t2, t3; | |||
int i; | |||
/* qhasm: z2 = z1^2^1 */ | |||
/* asm 1: fe_sq(>z2=fe#1,<z1=fe#11); for (i = 1;i < 1;++i) fe_sq(>z2=fe#1,>z2=fe#1); */ | |||
/* asm 2: fe_sq(>z2=t0,<z1=z); for (i = 1;i < 1;++i) fe_sq(>z2=t0,>z2=t0); */ | |||
fe_sq(out t0, ref z); //for (i = 1; i < 1; ++i) fe_sq(out t0, ref t0); | |||
/* qhasm: z8 = z2^2^2 */ | |||
/* asm 1: fe_sq(>z8=fe#2,<z2=fe#1); for (i = 1;i < 2;++i) fe_sq(>z8=fe#2,>z8=fe#2); */ | |||
/* asm 2: fe_sq(>z8=t1,<z2=t0); for (i = 1;i < 2;++i) fe_sq(>z8=t1,>z8=t1); */ | |||
fe_sq(out t1, ref t0); for (i = 1; i < 2; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z9 = z1*z8 */ | |||
/* asm 1: fe_mul(>z9=fe#2,<z1=fe#11,<z8=fe#2); */ | |||
/* asm 2: fe_mul(>z9=t1,<z1=z,<z8=t1); */ | |||
fe_mul(out t1, ref z, ref t1); | |||
/* qhasm: z11 = z2*z9 */ | |||
/* asm 1: fe_mul(>z11=fe#1,<z2=fe#1,<z9=fe#2); */ | |||
/* asm 2: fe_mul(>z11=t0,<z2=t0,<z9=t1); */ | |||
fe_mul(out t0, ref t0, ref t1); | |||
/* qhasm: z22 = z11^2^1 */ | |||
/* asm 1: fe_sq(>z22=fe#3,<z11=fe#1); for (i = 1;i < 1;++i) fe_sq(>z22=fe#3,>z22=fe#3); */ | |||
/* asm 2: fe_sq(>z22=t2,<z11=t0); for (i = 1;i < 1;++i) fe_sq(>z22=t2,>z22=t2); */ | |||
fe_sq(out t2, ref t0); //for (i = 1; i < 1; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_5_0 = z9*z22 */ | |||
/* asm 1: fe_mul(>z_5_0=fe#2,<z9=fe#2,<z22=fe#3); */ | |||
/* asm 2: fe_mul(>z_5_0=t1,<z9=t1,<z22=t2); */ | |||
fe_mul(out t1, ref t1, ref t2); | |||
/* qhasm: z_10_5 = z_5_0^2^5 */ | |||
/* asm 1: fe_sq(>z_10_5=fe#3,<z_5_0=fe#2); for (i = 1;i < 5;++i) fe_sq(>z_10_5=fe#3,>z_10_5=fe#3); */ | |||
/* asm 2: fe_sq(>z_10_5=t2,<z_5_0=t1); for (i = 1;i < 5;++i) fe_sq(>z_10_5=t2,>z_10_5=t2); */ | |||
fe_sq(out t2, ref t1); for (i = 1; i < 5; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_10_0 = z_10_5*z_5_0 */ | |||
/* asm 1: fe_mul(>z_10_0=fe#2,<z_10_5=fe#3,<z_5_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_10_0=t1,<z_10_5=t2,<z_5_0=t1); */ | |||
fe_mul(out t1, ref t2, ref t1); | |||
/* qhasm: z_20_10 = z_10_0^2^10 */ | |||
/* asm 1: fe_sq(>z_20_10=fe#3,<z_10_0=fe#2); for (i = 1;i < 10;++i) fe_sq(>z_20_10=fe#3,>z_20_10=fe#3); */ | |||
/* asm 2: fe_sq(>z_20_10=t2,<z_10_0=t1); for (i = 1;i < 10;++i) fe_sq(>z_20_10=t2,>z_20_10=t2); */ | |||
fe_sq(out t2, ref t1); for (i = 1; i < 10; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_20_0 = z_20_10*z_10_0 */ | |||
/* asm 1: fe_mul(>z_20_0=fe#3,<z_20_10=fe#3,<z_10_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_20_0=t2,<z_20_10=t2,<z_10_0=t1); */ | |||
fe_mul(out t2, ref t2, ref t1); | |||
/* qhasm: z_40_20 = z_20_0^2^20 */ | |||
/* asm 1: fe_sq(>z_40_20=fe#4,<z_20_0=fe#3); for (i = 1;i < 20;++i) fe_sq(>z_40_20=fe#4,>z_40_20=fe#4); */ | |||
/* asm 2: fe_sq(>z_40_20=t3,<z_20_0=t2); for (i = 1;i < 20;++i) fe_sq(>z_40_20=t3,>z_40_20=t3); */ | |||
fe_sq(out t3, ref t2); for (i = 1; i < 20; ++i) fe_sq(out t3, ref t3); | |||
/* qhasm: z_40_0 = z_40_20*z_20_0 */ | |||
/* asm 1: fe_mul(>z_40_0=fe#3,<z_40_20=fe#4,<z_20_0=fe#3); */ | |||
/* asm 2: fe_mul(>z_40_0=t2,<z_40_20=t3,<z_20_0=t2); */ | |||
fe_mul(out t2, ref t3, ref t2); | |||
/* qhasm: z_50_10 = z_40_0^2^10 */ | |||
/* asm 1: fe_sq(>z_50_10=fe#3,<z_40_0=fe#3); for (i = 1;i < 10;++i) fe_sq(>z_50_10=fe#3,>z_50_10=fe#3); */ | |||
/* asm 2: fe_sq(>z_50_10=t2,<z_40_0=t2); for (i = 1;i < 10;++i) fe_sq(>z_50_10=t2,>z_50_10=t2); */ | |||
fe_sq(out t2, ref t2); for (i = 1; i < 10; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_50_0 = z_50_10*z_10_0 */ | |||
/* asm 1: fe_mul(>z_50_0=fe#2,<z_50_10=fe#3,<z_10_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_50_0=t1,<z_50_10=t2,<z_10_0=t1); */ | |||
fe_mul(out t1, ref t2, ref t1); | |||
/* qhasm: z_100_50 = z_50_0^2^50 */ | |||
/* asm 1: fe_sq(>z_100_50=fe#3,<z_50_0=fe#2); for (i = 1;i < 50;++i) fe_sq(>z_100_50=fe#3,>z_100_50=fe#3); */ | |||
/* asm 2: fe_sq(>z_100_50=t2,<z_50_0=t1); for (i = 1;i < 50;++i) fe_sq(>z_100_50=t2,>z_100_50=t2); */ | |||
fe_sq(out t2, ref t1); for (i = 1; i < 50; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_100_0 = z_100_50*z_50_0 */ | |||
/* asm 1: fe_mul(>z_100_0=fe#3,<z_100_50=fe#3,<z_50_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_100_0=t2,<z_100_50=t2,<z_50_0=t1); */ | |||
fe_mul(out t2, ref t2, ref t1); | |||
/* qhasm: z_200_100 = z_100_0^2^100 */ | |||
/* asm 1: fe_sq(>z_200_100=fe#4,<z_100_0=fe#3); for (i = 1;i < 100;++i) fe_sq(>z_200_100=fe#4,>z_200_100=fe#4); */ | |||
/* asm 2: fe_sq(>z_200_100=t3,<z_100_0=t2); for (i = 1;i < 100;++i) fe_sq(>z_200_100=t3,>z_200_100=t3); */ | |||
fe_sq(out t3, ref t2); for (i = 1; i < 100; ++i) fe_sq(out t3, ref t3); | |||
/* qhasm: z_200_0 = z_200_100*z_100_0 */ | |||
/* asm 1: fe_mul(>z_200_0=fe#3,<z_200_100=fe#4,<z_100_0=fe#3); */ | |||
/* asm 2: fe_mul(>z_200_0=t2,<z_200_100=t3,<z_100_0=t2); */ | |||
fe_mul(out t2, ref t3, ref t2); | |||
/* qhasm: z_250_50 = z_200_0^2^50 */ | |||
/* asm 1: fe_sq(>z_250_50=fe#3,<z_200_0=fe#3); for (i = 1;i < 50;++i) fe_sq(>z_250_50=fe#3,>z_250_50=fe#3); */ | |||
/* asm 2: fe_sq(>z_250_50=t2,<z_200_0=t2); for (i = 1;i < 50;++i) fe_sq(>z_250_50=t2,>z_250_50=t2); */ | |||
fe_sq(out t2, ref t2); for (i = 1; i < 50; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_250_0 = z_250_50*z_50_0 */ | |||
/* asm 1: fe_mul(>z_250_0=fe#2,<z_250_50=fe#3,<z_50_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_250_0=t1,<z_250_50=t2,<z_50_0=t1); */ | |||
fe_mul(out t1, ref t2, ref t1); | |||
/* qhasm: z_255_5 = z_250_0^2^5 */ | |||
/* asm 1: fe_sq(>z_255_5=fe#2,<z_250_0=fe#2); for (i = 1;i < 5;++i) fe_sq(>z_255_5=fe#2,>z_255_5=fe#2); */ | |||
/* asm 2: fe_sq(>z_255_5=t1,<z_250_0=t1); for (i = 1;i < 5;++i) fe_sq(>z_255_5=t1,>z_255_5=t1); */ | |||
fe_sq(out t1, ref t1); for (i = 1; i < 5; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z_255_21 = z_255_5*z11 */ | |||
/* asm 1: fe_mul(>z_255_21=fe#12,<z_255_5=fe#2,<z11=fe#1); */ | |||
/* asm 2: fe_mul(>z_255_21=out,<z_255_5=t1,<z11=t0); */ | |||
fe_mul(out result, ref t1, ref t0); | |||
/* qhasm: return */ | |||
return; | |||
} | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
return 1 if f is in {1,3,5,...,q-2} | |||
return 0 if f is in {0,2,4,...,q-1} | |||
Preconditions: | |||
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | |||
*/ | |||
//int fe_isnegative(const fe f) | |||
public static int fe_isnegative(ref FieldElement f) | |||
{ | |||
FieldElement fr; | |||
fe_reduce(out fr, ref f); | |||
return fr.x0 & 1; | |||
} | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
return 1 if f == 0 | |||
return 0 if f != 0 | |||
Preconditions: | |||
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | |||
*/ | |||
// Todo: Discuss this with upstream | |||
// Above comment is from the original code. But I believe the original code returned | |||
// 0 if f == 0 | |||
// -1 if f != 0 | |||
// This code actually returns 0 if f==0 and 1 if f != 0 | |||
internal static int fe_isnonzero(ref FieldElement f) | |||
{ | |||
FieldElement fr; | |||
fe_reduce(out fr, ref f); | |||
int differentBits = 0; | |||
differentBits |= fr.x0; | |||
differentBits |= fr.x1; | |||
differentBits |= fr.x2; | |||
differentBits |= fr.x3; | |||
differentBits |= fr.x4; | |||
differentBits |= fr.x5; | |||
differentBits |= fr.x6; | |||
differentBits |= fr.x7; | |||
differentBits |= fr.x8; | |||
differentBits |= fr.x9; | |||
return (int)((unchecked((uint)differentBits - 1) >> 31) ^ 1); | |||
} | |||
} | |||
} |
@@ -0,0 +1,263 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = f * g | |||
Can overlap h with f or g. | |||
Preconditions: | |||
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. | |||
|g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. | |||
Postconditions: | |||
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. | |||
*/ | |||
/* | |||
Notes on implementation strategy: | |||
Using schoolbook multiplication. | |||
Karatsuba would save a little in some cost models. | |||
Most multiplications by 2 and 19 are 32-bit precomputations; | |||
cheaper than 64-bit postcomputations. | |||
There is one remaining multiplication by 19 in the carry chain; | |||
one *19 precomputation can be merged into this, | |||
but the resulting data flow is considerably less clean. | |||
There are 12 carries below. | |||
10 of them are 2-way parallelizable and vectorizable. | |||
Can get away with 11 carries, but then data flow is much deeper. | |||
With tighter constraints on inputs can squeeze carries into int32. | |||
*/ | |||
internal static void fe_mul(out FieldElement h, ref FieldElement f, ref FieldElement g) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int g0 = g.x0; | |||
int g1 = g.x1; | |||
int g2 = g.x2; | |||
int g3 = g.x3; | |||
int g4 = g.x4; | |||
int g5 = g.x5; | |||
int g6 = g.x6; | |||
int g7 = g.x7; | |||
int g8 = g.x8; | |||
int g9 = g.x9; | |||
int g1_19 = 19 * g1; /* 1.959375*2^29 */ | |||
int g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ | |||
int g3_19 = 19 * g3; | |||
int g4_19 = 19 * g4; | |||
int g5_19 = 19 * g5; | |||
int g6_19 = 19 * g6; | |||
int g7_19 = 19 * g7; | |||
int g8_19 = 19 * g8; | |||
int g9_19 = 19 * g9; | |||
int f1_2 = 2 * f1; | |||
int f3_2 = 2 * f3; | |||
int f5_2 = 2 * f5; | |||
int f7_2 = 2 * f7; | |||
int f9_2 = 2 * f9; | |||
long f0g0 = f0 * (long)g0; | |||
long f0g1 = f0 * (long)g1; | |||
long f0g2 = f0 * (long)g2; | |||
long f0g3 = f0 * (long)g3; | |||
long f0g4 = f0 * (long)g4; | |||
long f0g5 = f0 * (long)g5; | |||
long f0g6 = f0 * (long)g6; | |||
long f0g7 = f0 * (long)g7; | |||
long f0g8 = f0 * (long)g8; | |||
long f0g9 = f0 * (long)g9; | |||
long f1g0 = f1 * (long)g0; | |||
long f1g1_2 = f1_2 * (long)g1; | |||
long f1g2 = f1 * (long)g2; | |||
long f1g3_2 = f1_2 * (long)g3; | |||
long f1g4 = f1 * (long)g4; | |||
long f1g5_2 = f1_2 * (long)g5; | |||
long f1g6 = f1 * (long)g6; | |||
long f1g7_2 = f1_2 * (long)g7; | |||
long f1g8 = f1 * (long)g8; | |||
long f1g9_38 = f1_2 * (long)g9_19; | |||
long f2g0 = f2 * (long)g0; | |||
long f2g1 = f2 * (long)g1; | |||
long f2g2 = f2 * (long)g2; | |||
long f2g3 = f2 * (long)g3; | |||
long f2g4 = f2 * (long)g4; | |||
long f2g5 = f2 * (long)g5; | |||
long f2g6 = f2 * (long)g6; | |||
long f2g7 = f2 * (long)g7; | |||
long f2g8_19 = f2 * (long)g8_19; | |||
long f2g9_19 = f2 * (long)g9_19; | |||
long f3g0 = f3 * (long)g0; | |||
long f3g1_2 = f3_2 * (long)g1; | |||
long f3g2 = f3 * (long)g2; | |||
long f3g3_2 = f3_2 * (long)g3; | |||
long f3g4 = f3 * (long)g4; | |||
long f3g5_2 = f3_2 * (long)g5; | |||
long f3g6 = f3 * (long)g6; | |||
long f3g7_38 = f3_2 * (long)g7_19; | |||
long f3g8_19 = f3 * (long)g8_19; | |||
long f3g9_38 = f3_2 * (long)g9_19; | |||
long f4g0 = f4 * (long)g0; | |||
long f4g1 = f4 * (long)g1; | |||
long f4g2 = f4 * (long)g2; | |||
long f4g3 = f4 * (long)g3; | |||
long f4g4 = f4 * (long)g4; | |||
long f4g5 = f4 * (long)g5; | |||
long f4g6_19 = f4 * (long)g6_19; | |||
long f4g7_19 = f4 * (long)g7_19; | |||
long f4g8_19 = f4 * (long)g8_19; | |||
long f4g9_19 = f4 * (long)g9_19; | |||
long f5g0 = f5 * (long)g0; | |||
long f5g1_2 = f5_2 * (long)g1; | |||
long f5g2 = f5 * (long)g2; | |||
long f5g3_2 = f5_2 * (long)g3; | |||
long f5g4 = f5 * (long)g4; | |||
long f5g5_38 = f5_2 * (long)g5_19; | |||
long f5g6_19 = f5 * (long)g6_19; | |||
long f5g7_38 = f5_2 * (long)g7_19; | |||
long f5g8_19 = f5 * (long)g8_19; | |||
long f5g9_38 = f5_2 * (long)g9_19; | |||
long f6g0 = f6 * (long)g0; | |||
long f6g1 = f6 * (long)g1; | |||
long f6g2 = f6 * (long)g2; | |||
long f6g3 = f6 * (long)g3; | |||
long f6g4_19 = f6 * (long)g4_19; | |||
long f6g5_19 = f6 * (long)g5_19; | |||
long f6g6_19 = f6 * (long)g6_19; | |||
long f6g7_19 = f6 * (long)g7_19; | |||
long f6g8_19 = f6 * (long)g8_19; | |||
long f6g9_19 = f6 * (long)g9_19; | |||
long f7g0 = f7 * (long)g0; | |||
long f7g1_2 = f7_2 * (long)g1; | |||
long f7g2 = f7 * (long)g2; | |||
long f7g3_38 = f7_2 * (long)g3_19; | |||
long f7g4_19 = f7 * (long)g4_19; | |||
long f7g5_38 = f7_2 * (long)g5_19; | |||
long f7g6_19 = f7 * (long)g6_19; | |||
long f7g7_38 = f7_2 * (long)g7_19; | |||
long f7g8_19 = f7 * (long)g8_19; | |||
long f7g9_38 = f7_2 * (long)g9_19; | |||
long f8g0 = f8 * (long)g0; | |||
long f8g1 = f8 * (long)g1; | |||
long f8g2_19 = f8 * (long)g2_19; | |||
long f8g3_19 = f8 * (long)g3_19; | |||
long f8g4_19 = f8 * (long)g4_19; | |||
long f8g5_19 = f8 * (long)g5_19; | |||
long f8g6_19 = f8 * (long)g6_19; | |||
long f8g7_19 = f8 * (long)g7_19; | |||
long f8g8_19 = f8 * (long)g8_19; | |||
long f8g9_19 = f8 * (long)g9_19; | |||
long f9g0 = f9 * (long)g0; | |||
long f9g1_38 = f9_2 * (long)g1_19; | |||
long f9g2_19 = f9 * (long)g2_19; | |||
long f9g3_38 = f9_2 * (long)g3_19; | |||
long f9g4_19 = f9 * (long)g4_19; | |||
long f9g5_38 = f9_2 * (long)g5_19; | |||
long f9g6_19 = f9 * (long)g6_19; | |||
long f9g7_38 = f9_2 * (long)g7_19; | |||
long f9g8_19 = f9 * (long)g8_19; | |||
long f9g9_38 = f9_2 * (long)g9_19; | |||
long h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; | |||
long h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; | |||
long h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; | |||
long h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; | |||
long h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; | |||
long h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; | |||
long h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; | |||
long h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; | |||
long h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; | |||
long h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0; | |||
long carry0; | |||
long carry1; | |||
long carry2; | |||
long carry3; | |||
long carry4; | |||
long carry5; | |||
long carry6; | |||
long carry7; | |||
long carry8; | |||
long carry9; | |||
/* | |||
|h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38)) | |||
i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8 | |||
|h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19)) | |||
i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 | |||
*/ | |||
carry0 = (h0 + (long)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
carry4 = (h4 + (long)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
/* |h0| <= 2^25 */ | |||
/* |h4| <= 2^25 */ | |||
/* |h1| <= 1.71*2^59 */ | |||
/* |h5| <= 1.71*2^59 */ | |||
carry1 = (h1 + (long)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
carry5 = (h5 + (long)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
/* |h1| <= 2^24; from now on fits into int32 */ | |||
/* |h5| <= 2^24; from now on fits into int32 */ | |||
/* |h2| <= 1.41*2^60 */ | |||
/* |h6| <= 1.41*2^60 */ | |||
carry2 = (h2 + (long)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
carry6 = (h6 + (long)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
/* |h2| <= 2^25; from now on fits into int32 unchanged */ | |||
/* |h6| <= 2^25; from now on fits into int32 unchanged */ | |||
/* |h3| <= 1.71*2^59 */ | |||
/* |h7| <= 1.71*2^59 */ | |||
carry3 = (h3 + (long)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
carry7 = (h7 + (long)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
/* |h3| <= 2^24; from now on fits into int32 unchanged */ | |||
/* |h7| <= 2^24; from now on fits into int32 unchanged */ | |||
/* |h4| <= 1.72*2^34 */ | |||
/* |h8| <= 1.41*2^60 */ | |||
carry4 = (h4 + (long)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
carry8 = (h8 + (long)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
/* |h4| <= 2^25; from now on fits into int32 unchanged */ | |||
/* |h8| <= 2^25; from now on fits into int32 unchanged */ | |||
/* |h5| <= 1.01*2^24 */ | |||
/* |h9| <= 1.71*2^59 */ | |||
carry9 = (h9 + (long)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; | |||
/* |h9| <= 2^24; from now on fits into int32 unchanged */ | |||
/* |h0| <= 1.1*2^39 */ | |||
carry0 = (h0 + (long)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
/* |h0| <= 2^25; from now on fits into int32 unchanged */ | |||
/* |h1| <= 1.01*2^24 */ | |||
h.x0 = (int)h0; | |||
h.x1 = (int)h1; | |||
h.x2 = (int)h2; | |||
h.x3 = (int)h3; | |||
h.x4 = (int)h4; | |||
h.x5 = (int)h5; | |||
h.x6 = (int)h6; | |||
h.x7 = (int)h7; | |||
h.x8 = (int)h8; | |||
h.x9 = (int)h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = f * 121666 | |||
Can overlap h with f. | |||
Preconditions: | |||
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | |||
Postconditions: | |||
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
*/ | |||
public static void fe_mul121666(out FieldElement h, ref FieldElement f) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
var h0 = f0 * 121666L; | |||
var h1 = f1 * 121666L; | |||
var h2 = f2 * 121666L; | |||
var h3 = f3 * 121666L; | |||
var h4 = f4 * 121666L; | |||
var h5 = f5 * 121666L; | |||
var h6 = f6 * 121666L; | |||
var h7 = f7 * 121666L; | |||
var h8 = f8 * 121666L; | |||
var h9 = f9 * 121666L; | |||
var carry9 = (h9 + (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; | |||
var carry1 = (h1 + (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
var carry3 = (h3 + (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
var carry5 = (h5 + (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
var carry7 = (h7 + (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
var carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
var carry2 = (h2 + (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
var carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry6 = (h6 + (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
var carry8 = (h8 + (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
h.x0 = (int)h0; | |||
h.x1 = (int)h1; | |||
h.x2 = (int)h2; | |||
h.x3 = (int)h3; | |||
h.x4 = (int)h4; | |||
h.x5 = (int)h5; | |||
h.x6 = (int)h6; | |||
h.x7 = (int)h7; | |||
h.x8 = (int)h8; | |||
h.x9 = (int)h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = -f | |||
Preconditions: | |||
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
Postconditions: | |||
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
*/ | |||
internal static void fe_neg(out FieldElement h, ref FieldElement f) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int h0 = -f0; | |||
int h1 = -f1; | |||
int h2 = -f2; | |||
int h3 = -f3; | |||
int h4 = -f4; | |||
int h5 = -f5; | |||
int h6 = -f6; | |||
int h7 = -f7; | |||
int h8 = -f8; | |||
int h9 = -f9; | |||
h.x0 = h0; | |||
h.x1 = h1; | |||
h.x2 = h2; | |||
h.x3 = h3; | |||
h.x4 = h4; | |||
h.x5 = h5; | |||
h.x6 = h6; | |||
h.x7 = h7; | |||
h.x8 = h8; | |||
h.x9 = h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,125 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
internal static void fe_pow22523(out FieldElement result, ref FieldElement z) | |||
{ | |||
FieldElement t0, t1, t2; | |||
int i; | |||
/* qhasm: z2 = z1^2^1 */ | |||
/* asm 1: fe_sq(>z2=fe#1,<z1=fe#11); for (i = 1;i < 1;++i) fe_sq(>z2=fe#1,>z2=fe#1); */ | |||
/* asm 2: fe_sq(>z2=t0,<z1=z); for (i = 1;i < 1;++i) fe_sq(>z2=t0,>z2=t0); */ | |||
fe_sq(out t0, ref z); //for (i = 1; i < 1; ++i) fe_sq(out t0, ref t0); | |||
/* qhasm: z8 = z2^2^2 */ | |||
/* asm 1: fe_sq(>z8=fe#2,<z2=fe#1); for (i = 1;i < 2;++i) fe_sq(>z8=fe#2,>z8=fe#2); */ | |||
/* asm 2: fe_sq(>z8=t1,<z2=t0); for (i = 1;i < 2;++i) fe_sq(>z8=t1,>z8=t1); */ | |||
fe_sq(out t1, ref t0); for (i = 1; i < 2; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z9 = z1*z8 */ | |||
/* asm 1: fe_mul(>z9=fe#2,<z1=fe#11,<z8=fe#2); */ | |||
/* asm 2: fe_mul(>z9=t1,<z1=z,<z8=t1); */ | |||
fe_mul(out t1, ref z, ref t1); | |||
/* qhasm: z11 = z2*z9 */ | |||
/* asm 1: fe_mul(>z11=fe#1,<z2=fe#1,<z9=fe#2); */ | |||
/* asm 2: fe_mul(>z11=t0,<z2=t0,<z9=t1); */ | |||
fe_mul(out t0, ref t0, ref t1); | |||
/* qhasm: z22 = z11^2^1 */ | |||
/* asm 1: fe_sq(>z22=fe#1,<z11=fe#1); for (i = 1;i < 1;++i) fe_sq(>z22=fe#1,>z22=fe#1); */ | |||
/* asm 2: fe_sq(>z22=t0,<z11=t0); for (i = 1;i < 1;++i) fe_sq(>z22=t0,>z22=t0); */ | |||
fe_sq(out t0, ref t0); //for (i = 1; i < 1; ++i) fe_sq(out t0, ref t0); | |||
/* qhasm: z_5_0 = z9*z22 */ | |||
/* asm 1: fe_mul(>z_5_0=fe#1,<z9=fe#2,<z22=fe#1); */ | |||
/* asm 2: fe_mul(>z_5_0=t0,<z9=t1,<z22=t0); */ | |||
fe_mul(out t0, ref t1, ref t0); | |||
/* qhasm: z_10_5 = z_5_0^2^5 */ | |||
/* asm 1: fe_sq(>z_10_5=fe#2,<z_5_0=fe#1); for (i = 1;i < 5;++i) fe_sq(>z_10_5=fe#2,>z_10_5=fe#2); */ | |||
/* asm 2: fe_sq(>z_10_5=t1,<z_5_0=t0); for (i = 1;i < 5;++i) fe_sq(>z_10_5=t1,>z_10_5=t1); */ | |||
fe_sq(out t1, ref t0); for (i = 1; i < 5; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z_10_0 = z_10_5*z_5_0 */ | |||
/* asm 1: fe_mul(>z_10_0=fe#1,<z_10_5=fe#2,<z_5_0=fe#1); */ | |||
/* asm 2: fe_mul(>z_10_0=t0,<z_10_5=t1,<z_5_0=t0); */ | |||
fe_mul(out t0, ref t1, ref t0); | |||
/* qhasm: z_20_10 = z_10_0^2^10 */ | |||
/* asm 1: fe_sq(>z_20_10=fe#2,<z_10_0=fe#1); for (i = 1;i < 10;++i) fe_sq(>z_20_10=fe#2,>z_20_10=fe#2); */ | |||
/* asm 2: fe_sq(>z_20_10=t1,<z_10_0=t0); for (i = 1;i < 10;++i) fe_sq(>z_20_10=t1,>z_20_10=t1); */ | |||
fe_sq(out t1, ref t0); for (i = 1; i < 10; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z_20_0 = z_20_10*z_10_0 */ | |||
/* asm 1: fe_mul(>z_20_0=fe#2,<z_20_10=fe#2,<z_10_0=fe#1); */ | |||
/* asm 2: fe_mul(>z_20_0=t1,<z_20_10=t1,<z_10_0=t0); */ | |||
fe_mul(out t1, ref t1, ref t0); | |||
/* qhasm: z_40_20 = z_20_0^2^20 */ | |||
/* asm 1: fe_sq(>z_40_20=fe#3,<z_20_0=fe#2); for (i = 1;i < 20;++i) fe_sq(>z_40_20=fe#3,>z_40_20=fe#3); */ | |||
/* asm 2: fe_sq(>z_40_20=t2,<z_20_0=t1); for (i = 1;i < 20;++i) fe_sq(>z_40_20=t2,>z_40_20=t2); */ | |||
fe_sq(out t2, ref t1); for (i = 1; i < 20; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_40_0 = z_40_20*z_20_0 */ | |||
/* asm 1: fe_mul(>z_40_0=fe#2,<z_40_20=fe#3,<z_20_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_40_0=t1,<z_40_20=t2,<z_20_0=t1); */ | |||
fe_mul(out t1, ref t2, ref t1); | |||
/* qhasm: z_50_10 = z_40_0^2^10 */ | |||
/* asm 1: fe_sq(>z_50_10=fe#2,<z_40_0=fe#2); for (i = 1;i < 10;++i) fe_sq(>z_50_10=fe#2,>z_50_10=fe#2); */ | |||
/* asm 2: fe_sq(>z_50_10=t1,<z_40_0=t1); for (i = 1;i < 10;++i) fe_sq(>z_50_10=t1,>z_50_10=t1); */ | |||
fe_sq(out t1, ref t1); for (i = 1; i < 10; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z_50_0 = z_50_10*z_10_0 */ | |||
/* asm 1: fe_mul(>z_50_0=fe#1,<z_50_10=fe#2,<z_10_0=fe#1); */ | |||
/* asm 2: fe_mul(>z_50_0=t0,<z_50_10=t1,<z_10_0=t0); */ | |||
fe_mul(out t0, ref t1, ref t0); | |||
/* qhasm: z_100_50 = z_50_0^2^50 */ | |||
/* asm 1: fe_sq(>z_100_50=fe#2,<z_50_0=fe#1); for (i = 1;i < 50;++i) fe_sq(>z_100_50=fe#2,>z_100_50=fe#2); */ | |||
/* asm 2: fe_sq(>z_100_50=t1,<z_50_0=t0); for (i = 1;i < 50;++i) fe_sq(>z_100_50=t1,>z_100_50=t1); */ | |||
fe_sq(out t1, ref t0); for (i = 1; i < 50; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z_100_0 = z_100_50*z_50_0 */ | |||
/* asm 1: fe_mul(>z_100_0=fe#2,<z_100_50=fe#2,<z_50_0=fe#1); */ | |||
/* asm 2: fe_mul(>z_100_0=t1,<z_100_50=t1,<z_50_0=t0); */ | |||
fe_mul(out t1, ref t1, ref t0); | |||
/* qhasm: z_200_100 = z_100_0^2^100 */ | |||
/* asm 1: fe_sq(>z_200_100=fe#3,<z_100_0=fe#2); for (i = 1;i < 100;++i) fe_sq(>z_200_100=fe#3,>z_200_100=fe#3); */ | |||
/* asm 2: fe_sq(>z_200_100=t2,<z_100_0=t1); for (i = 1;i < 100;++i) fe_sq(>z_200_100=t2,>z_200_100=t2); */ | |||
fe_sq(out t2, ref t1); for (i = 1; i < 100; ++i) fe_sq(out t2, ref t2); | |||
/* qhasm: z_200_0 = z_200_100*z_100_0 */ | |||
/* asm 1: fe_mul(>z_200_0=fe#2,<z_200_100=fe#3,<z_100_0=fe#2); */ | |||
/* asm 2: fe_mul(>z_200_0=t1,<z_200_100=t2,<z_100_0=t1); */ | |||
fe_mul(out t1, ref t2, ref t1); | |||
/* qhasm: z_250_50 = z_200_0^2^50 */ | |||
/* asm 1: fe_sq(>z_250_50=fe#2,<z_200_0=fe#2); for (i = 1;i < 50;++i) fe_sq(>z_250_50=fe#2,>z_250_50=fe#2); */ | |||
/* asm 2: fe_sq(>z_250_50=t1,<z_200_0=t1); for (i = 1;i < 50;++i) fe_sq(>z_250_50=t1,>z_250_50=t1); */ | |||
fe_sq(out t1, ref t1); for (i = 1; i < 50; ++i) fe_sq(out t1, ref t1); | |||
/* qhasm: z_250_0 = z_250_50*z_50_0 */ | |||
/* asm 1: fe_mul(>z_250_0=fe#1,<z_250_50=fe#2,<z_50_0=fe#1); */ | |||
/* asm 2: fe_mul(>z_250_0=t0,<z_250_50=t1,<z_50_0=t0); */ | |||
fe_mul(out t0, ref t1, ref t0); | |||
/* qhasm: z_252_2 = z_250_0^2^2 */ | |||
/* asm 1: fe_sq(>z_252_2=fe#1,<z_250_0=fe#1); for (i = 1;i < 2;++i) fe_sq(>z_252_2=fe#1,>z_252_2=fe#1); */ | |||
/* asm 2: fe_sq(>z_252_2=t0,<z_250_0=t0); for (i = 1;i < 2;++i) fe_sq(>z_252_2=t0,>z_252_2=t0); */ | |||
fe_sq(out t0, ref t0); for (i = 1; i < 2; ++i) fe_sq(out t0, ref t0); | |||
/* qhasm: z_252_3 = z_252_2*z1 */ | |||
/* asm 1: fe_mul(>z_252_3=fe#12,<z_252_2=fe#1,<z1=fe#11); */ | |||
/* asm 2: fe_mul(>z_252_3=out,<z_252_2=t0,<z1=z); */ | |||
fe_mul(out result, ref t0, ref z); | |||
/* qhasm: return */ | |||
} | |||
} | |||
} |
@@ -0,0 +1,143 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = f * f | |||
Can overlap h with f. | |||
Preconditions: | |||
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. | |||
Postconditions: | |||
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. | |||
*/ | |||
/* | |||
See fe_mul.c for discussion of implementation strategy. | |||
*/ | |||
internal static void fe_sq(out FieldElement h, ref FieldElement f) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int f0_2 = 2 * f0; | |||
int f1_2 = 2 * f1; | |||
int f2_2 = 2 * f2; | |||
int f3_2 = 2 * f3; | |||
int f4_2 = 2 * f4; | |||
int f5_2 = 2 * f5; | |||
int f6_2 = 2 * f6; | |||
int f7_2 = 2 * f7; | |||
int f5_38 = 38 * f5; /* 1.959375*2^30 */ | |||
int f6_19 = 19 * f6; /* 1.959375*2^30 */ | |||
int f7_38 = 38 * f7; /* 1.959375*2^30 */ | |||
int f8_19 = 19 * f8; /* 1.959375*2^30 */ | |||
int f9_38 = 38 * f9; /* 1.959375*2^30 */ | |||
var f0f0 = f0 * (long)f0; | |||
var f0f1_2 = f0_2 * (long)f1; | |||
var f0f2_2 = f0_2 * (long)f2; | |||
var f0f3_2 = f0_2 * (long)f3; | |||
var f0f4_2 = f0_2 * (long)f4; | |||
var f0f5_2 = f0_2 * (long)f5; | |||
var f0f6_2 = f0_2 * (long)f6; | |||
var f0f7_2 = f0_2 * (long)f7; | |||
var f0f8_2 = f0_2 * (long)f8; | |||
var f0f9_2 = f0_2 * (long)f9; | |||
var f1f1_2 = f1_2 * (long)f1; | |||
var f1f2_2 = f1_2 * (long)f2; | |||
var f1f3_4 = f1_2 * (long)f3_2; | |||
var f1f4_2 = f1_2 * (long)f4; | |||
var f1f5_4 = f1_2 * (long)f5_2; | |||
var f1f6_2 = f1_2 * (long)f6; | |||
var f1f7_4 = f1_2 * (long)f7_2; | |||
var f1f8_2 = f1_2 * (long)f8; | |||
var f1f9_76 = f1_2 * (long)f9_38; | |||
var f2f2 = f2 * (long)f2; | |||
var f2f3_2 = f2_2 * (long)f3; | |||
var f2f4_2 = f2_2 * (long)f4; | |||
var f2f5_2 = f2_2 * (long)f5; | |||
var f2f6_2 = f2_2 * (long)f6; | |||
var f2f7_2 = f2_2 * (long)f7; | |||
var f2f8_38 = f2_2 * (long)f8_19; | |||
var f2f9_38 = f2 * (long)f9_38; | |||
var f3f3_2 = f3_2 * (long)f3; | |||
var f3f4_2 = f3_2 * (long)f4; | |||
var f3f5_4 = f3_2 * (long)f5_2; | |||
var f3f6_2 = f3_2 * (long)f6; | |||
var f3f7_76 = f3_2 * (long)f7_38; | |||
var f3f8_38 = f3_2 * (long)f8_19; | |||
var f3f9_76 = f3_2 * (long)f9_38; | |||
var f4f4 = f4 * (long)f4; | |||
var f4f5_2 = f4_2 * (long)f5; | |||
var f4f6_38 = f4_2 * (long)f6_19; | |||
var f4f7_38 = f4 * (long)f7_38; | |||
var f4f8_38 = f4_2 * (long)f8_19; | |||
var f4f9_38 = f4 * (long)f9_38; | |||
var f5f5_38 = f5 * (long)f5_38; | |||
var f5f6_38 = f5_2 * (long)f6_19; | |||
var f5f7_76 = f5_2 * (long)f7_38; | |||
var f5f8_38 = f5_2 * (long)f8_19; | |||
var f5f9_76 = f5_2 * (long)f9_38; | |||
var f6f6_19 = f6 * (long)f6_19; | |||
var f6f7_38 = f6 * (long)f7_38; | |||
var f6f8_38 = f6_2 * (long)f8_19; | |||
var f6f9_38 = f6 * (long)f9_38; | |||
var f7f7_38 = f7 * (long)f7_38; | |||
var f7f8_38 = f7_2 * (long)f8_19; | |||
var f7f9_76 = f7_2 * (long)f9_38; | |||
var f8f8_19 = f8 * (long)f8_19; | |||
var f8f9_38 = f8 * (long)f9_38; | |||
var f9f9_38 = f9 * (long)f9_38; | |||
var h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; | |||
var h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; | |||
var h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; | |||
var h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; | |||
var h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; | |||
var h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; | |||
var h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; | |||
var h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; | |||
var h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; | |||
var h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; | |||
var carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
var carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry1 = (h1 + (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
var carry5 = (h5 + (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
var carry2 = (h2 + (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
var carry6 = (h6 + (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
var carry3 = (h3 + (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
var carry7 = (h7 + (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry8 = (h8 + (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
var carry9 = (h9 + (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; | |||
carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
h.x0 = (int)h0; | |||
h.x1 = (int)h1; | |||
h.x2 = (int)h2; | |||
h.x3 = (int)h3; | |||
h.x4 = (int)h4; | |||
h.x5 = (int)h5; | |||
h.x6 = (int)h6; | |||
h.x7 = (int)h7; | |||
h.x8 = (int)h8; | |||
h.x9 = (int)h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,154 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = 2 * f * f | |||
Can overlap h with f. | |||
Preconditions: | |||
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. | |||
Postconditions: | |||
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. | |||
*/ | |||
/* | |||
See fe_mul.c for discussion of implementation strategy. | |||
*/ | |||
internal static void fe_sq2(out FieldElement h, ref FieldElement f) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int f0_2 = 2 * f0; | |||
int f1_2 = 2 * f1; | |||
int f2_2 = 2 * f2; | |||
int f3_2 = 2 * f3; | |||
int f4_2 = 2 * f4; | |||
int f5_2 = 2 * f5; | |||
int f6_2 = 2 * f6; | |||
int f7_2 = 2 * f7; | |||
int f5_38 = 38 * f5; /* 1.959375*2^30 */ | |||
int f6_19 = 19 * f6; /* 1.959375*2^30 */ | |||
int f7_38 = 38 * f7; /* 1.959375*2^30 */ | |||
int f8_19 = 19 * f8; /* 1.959375*2^30 */ | |||
int f9_38 = 38 * f9; /* 1.959375*2^30 */ | |||
var f0f0 = f0 * (long)f0; | |||
var f0f1_2 = f0_2 * (long)f1; | |||
var f0f2_2 = f0_2 * (long)f2; | |||
var f0f3_2 = f0_2 * (long)f3; | |||
var f0f4_2 = f0_2 * (long)f4; | |||
var f0f5_2 = f0_2 * (long)f5; | |||
var f0f6_2 = f0_2 * (long)f6; | |||
var f0f7_2 = f0_2 * (long)f7; | |||
var f0f8_2 = f0_2 * (long)f8; | |||
var f0f9_2 = f0_2 * (long)f9; | |||
var f1f1_2 = f1_2 * (long)f1; | |||
var f1f2_2 = f1_2 * (long)f2; | |||
var f1f3_4 = f1_2 * (long)f3_2; | |||
var f1f4_2 = f1_2 * (long)f4; | |||
var f1f5_4 = f1_2 * (long)f5_2; | |||
var f1f6_2 = f1_2 * (long)f6; | |||
var f1f7_4 = f1_2 * (long)f7_2; | |||
var f1f8_2 = f1_2 * (long)f8; | |||
var f1f9_76 = f1_2 * (long)f9_38; | |||
var f2f2 = f2 * (long)f2; | |||
var f2f3_2 = f2_2 * (long)f3; | |||
var f2f4_2 = f2_2 * (long)f4; | |||
var f2f5_2 = f2_2 * (long)f5; | |||
var f2f6_2 = f2_2 * (long)f6; | |||
var f2f7_2 = f2_2 * (long)f7; | |||
var f2f8_38 = f2_2 * (long)f8_19; | |||
var f2f9_38 = f2 * (long)f9_38; | |||
var f3f3_2 = f3_2 * (long)f3; | |||
var f3f4_2 = f3_2 * (long)f4; | |||
var f3f5_4 = f3_2 * (long)f5_2; | |||
var f3f6_2 = f3_2 * (long)f6; | |||
var f3f7_76 = f3_2 * (long)f7_38; | |||
var f3f8_38 = f3_2 * (long)f8_19; | |||
var f3f9_76 = f3_2 * (long)f9_38; | |||
var f4f4 = f4 * (long)f4; | |||
var f4f5_2 = f4_2 * (long)f5; | |||
var f4f6_38 = f4_2 * (long)f6_19; | |||
var f4f7_38 = f4 * (long)f7_38; | |||
var f4f8_38 = f4_2 * (long)f8_19; | |||
var f4f9_38 = f4 * (long)f9_38; | |||
var f5f5_38 = f5 * (long)f5_38; | |||
var f5f6_38 = f5_2 * (long)f6_19; | |||
var f5f7_76 = f5_2 * (long)f7_38; | |||
var f5f8_38 = f5_2 * (long)f8_19; | |||
var f5f9_76 = f5_2 * (long)f9_38; | |||
var f6f6_19 = f6 * (long)f6_19; | |||
var f6f7_38 = f6 * (long)f7_38; | |||
var f6f8_38 = f6_2 * (long)f8_19; | |||
var f6f9_38 = f6 * (long)f9_38; | |||
var f7f7_38 = f7 * (long)f7_38; | |||
var f7f8_38 = f7_2 * (long)f8_19; | |||
var f7f9_76 = f7_2 * (long)f9_38; | |||
var f8f8_19 = f8 * (long)f8_19; | |||
var f8f9_38 = f8 * (long)f9_38; | |||
var f9f9_38 = f9 * (long)f9_38; | |||
var h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; | |||
var h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; | |||
var h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; | |||
var h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; | |||
var h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; | |||
var h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; | |||
var h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; | |||
var h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; | |||
var h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; | |||
var h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; | |||
h0 += h0; | |||
h1 += h1; | |||
h2 += h2; | |||
h3 += h3; | |||
h4 += h4; | |||
h5 += h5; | |||
h6 += h6; | |||
h7 += h7; | |||
h8 += h8; | |||
h9 += h9; | |||
var carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
var carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry1 = (h1 + (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
var carry5 = (h5 + (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
var carry2 = (h2 + (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
var carry6 = (h6 + (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
var carry3 = (h3 + (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
var carry7 = (h7 + (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
carry4 = (h4 + (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry8 = (h8 + (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
var carry9 = (h9 + (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; | |||
carry0 = (h0 + (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
h.x0 = (int)h0; | |||
h.x1 = (int)h1; | |||
h.x2 = (int)h2; | |||
h.x3 = (int)h3; | |||
h.x4 = (int)h4; | |||
h.x5 = (int)h5; | |||
h.x6 = (int)h6; | |||
h.x7 = (int)h7; | |||
h.x8 = (int)h8; | |||
h.x9 = (int)h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
h = f - g | |||
Can overlap h with f or g. | |||
Preconditions: | |||
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. | |||
Postconditions: | |||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | |||
*/ | |||
internal static void fe_sub(out FieldElement h, ref FieldElement f, ref FieldElement g) | |||
{ | |||
int f0 = f.x0; | |||
int f1 = f.x1; | |||
int f2 = f.x2; | |||
int f3 = f.x3; | |||
int f4 = f.x4; | |||
int f5 = f.x5; | |||
int f6 = f.x6; | |||
int f7 = f.x7; | |||
int f8 = f.x8; | |||
int f9 = f.x9; | |||
int g0 = g.x0; | |||
int g1 = g.x1; | |||
int g2 = g.x2; | |||
int g3 = g.x3; | |||
int g4 = g.x4; | |||
int g5 = g.x5; | |||
int g6 = g.x6; | |||
int g7 = g.x7; | |||
int g8 = g.x8; | |||
int g9 = g.x9; | |||
int h0 = f0 - g0; | |||
int h1 = f1 - g1; | |||
int h2 = f2 - g2; | |||
int h3 = f3 - g3; | |||
int h4 = f4 - g4; | |||
int h5 = f5 - g5; | |||
int h6 = f6 - g6; | |||
int h7 = f7 - g7; | |||
int h8 = f8 - g8; | |||
int h9 = f9 - g9; | |||
h.x0 = h0; | |||
h.x1 = h1; | |||
h.x2 = h2; | |||
h.x3 = h3; | |||
h.x4 = h4; | |||
h.x5 = h5; | |||
h.x6 = h6; | |||
h.x7 = h7; | |||
h.x8 = h8; | |||
h.x9 = h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,145 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class FieldOperations | |||
{ | |||
/* | |||
Preconditions: | |||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. | |||
Write p=2^255-19; q=floor(h/p). | |||
Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). | |||
Proof: | |||
Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. | |||
Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. | |||
Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). | |||
Then 0<y<1. | |||
Write r=h-pq. | |||
Have 0<=r<=p-1=2^255-20. | |||
Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. | |||
Write x=r+19(2^-255)r+y. | |||
Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. | |||
Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) | |||
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. | |||
*/ | |||
internal static void fe_tobytes(byte[] s, int offset, ref FieldElement h) | |||
{ | |||
FieldElement hr; | |||
fe_reduce(out hr, ref h); | |||
int h0 = hr.x0; | |||
int h1 = hr.x1; | |||
int h2 = hr.x2; | |||
int h3 = hr.x3; | |||
int h4 = hr.x4; | |||
int h5 = hr.x5; | |||
int h6 = hr.x6; | |||
int h7 = hr.x7; | |||
int h8 = hr.x8; | |||
int h9 = hr.x9; | |||
/* | |||
Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. | |||
Have h0+...+2^230 h9 between 0 and 2^255-1; | |||
evidently 2^255 h10-2^255 q = 0. | |||
Goal: Output h0+...+2^230 h9. | |||
*/ | |||
unchecked | |||
{ | |||
s[offset + 0] = (byte) (h0 >> 0); | |||
s[offset + 1] = (byte) (h0 >> 8); | |||
s[offset + 2] = (byte) (h0 >> 16); | |||
s[offset + 3] = (byte) ((h0 >> 24) | (h1 << 2)); | |||
s[offset + 4] = (byte) (h1 >> 6); | |||
s[offset + 5] = (byte) (h1 >> 14); | |||
s[offset + 6] = (byte) ((h1 >> 22) | (h2 << 3)); | |||
s[offset + 7] = (byte) (h2 >> 5); | |||
s[offset + 8] = (byte) (h2 >> 13); | |||
s[offset + 9] = (byte) ((h2 >> 21) | (h3 << 5)); | |||
s[offset + 10] = (byte) (h3 >> 3); | |||
s[offset + 11] = (byte) (h3 >> 11); | |||
s[offset + 12] = (byte) ((h3 >> 19) | (h4 << 6)); | |||
s[offset + 13] = (byte) (h4 >> 2); | |||
s[offset + 14] = (byte) (h4 >> 10); | |||
s[offset + 15] = (byte) (h4 >> 18); | |||
s[offset + 16] = (byte) (h5 >> 0); | |||
s[offset + 17] = (byte) (h5 >> 8); | |||
s[offset + 18] = (byte) (h5 >> 16); | |||
s[offset + 19] = (byte) ((h5 >> 24) | (h6 << 1)); | |||
s[offset + 20] = (byte) (h6 >> 7); | |||
s[offset + 21] = (byte) (h6 >> 15); | |||
s[offset + 22] = (byte) ((h6 >> 23) | (h7 << 3)); | |||
s[offset + 23] = (byte) (h7 >> 5); | |||
s[offset + 24] = (byte) (h7 >> 13); | |||
s[offset + 25] = (byte) ((h7 >> 21) | (h8 << 4)); | |||
s[offset + 26] = (byte) (h8 >> 4); | |||
s[offset + 27] = (byte) (h8 >> 12); | |||
s[offset + 28] = (byte) ((h8 >> 20) | (h9 << 6)); | |||
s[offset + 29] = (byte) (h9 >> 2); | |||
s[offset + 30] = (byte) (h9 >> 10); | |||
s[offset + 31] = (byte) (h9 >> 18); | |||
} | |||
} | |||
internal static void fe_reduce(out FieldElement hr, ref FieldElement h) | |||
{ | |||
int h0 = h.x0; | |||
int h1 = h.x1; | |||
int h2 = h.x2; | |||
int h3 = h.x3; | |||
int h4 = h.x4; | |||
int h5 = h.x5; | |||
int h6 = h.x6; | |||
int h7 = h.x7; | |||
int h8 = h.x8; | |||
int h9 = h.x9; | |||
int q; | |||
q = (19 * h9 + (1 << 24)) >> 25; | |||
q = (h0 + q) >> 26; | |||
q = (h1 + q) >> 25; | |||
q = (h2 + q) >> 26; | |||
q = (h3 + q) >> 25; | |||
q = (h4 + q) >> 26; | |||
q = (h5 + q) >> 25; | |||
q = (h6 + q) >> 26; | |||
q = (h7 + q) >> 25; | |||
q = (h8 + q) >> 26; | |||
q = (h9 + q) >> 25; | |||
/* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ | |||
h0 += 19 * q; | |||
/* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ | |||
var carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; | |||
var carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; | |||
var carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; | |||
var carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; | |||
var carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; | |||
var carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; | |||
var carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; | |||
var carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; | |||
var carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; | |||
var carry9 = h9 >> 25; h9 -= carry9 << 25; | |||
/* h10 = carry9 */ | |||
hr.x0 = h0; | |||
hr.x1 = h1; | |||
hr.x2 = h2; | |||
hr.x3 = h3; | |||
hr.x4 = h4; | |||
hr.x5 = h5; | |||
hr.x6 = h6; | |||
hr.x7 = h7; | |||
hr.x8 = h8; | |||
hr.x9 = h9; | |||
} | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p + q | |||
*/ | |||
internal static void ge_add(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementCached q) | |||
{ | |||
FieldElement t0; | |||
/* qhasm: YpX1 = Y1+X1 */ | |||
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_add(out r.X, ref p.Y, ref p.X); | |||
/* qhasm: YmX1 = Y1-X1 */ | |||
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_sub(out r.Y, ref p.Y, ref p.X); | |||
/* qhasm: A = YpX1*YpX2 */ | |||
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YpX2=fe#15); */ | |||
/* asm 2: fe_mul(>A=r.Z,<YpX1=r.X,<YpX2=q.YplusX); */ | |||
FieldOperations.fe_mul(out r.Z, ref r.X, ref q.YplusX); | |||
/* qhasm: B = YmX1*YmX2 */ | |||
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YmX2=fe#16); */ | |||
/* asm 2: fe_mul(>B=r.Y,<YmX1=r.Y,<YmX2=q.YminusX); */ | |||
FieldOperations.fe_mul(out r.Y, ref r.Y, ref q.YminusX); | |||
/* qhasm: C = T2d2*T1 */ | |||
/* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */ | |||
/* asm 2: fe_mul(>C=r.T,<T2d2=q.T2d,<T1=p.T); */ | |||
FieldOperations.fe_mul(out r.T, ref q.T2d, ref p.T); | |||
/* qhasm: ZZ = Z1*Z2 */ | |||
/* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */ | |||
/* asm 2: fe_mul(>ZZ=r.X,<Z1=p.Z,<Z2=q.Z); */ | |||
FieldOperations.fe_mul(out r.X, ref p.Z, ref q.Z); | |||
/* qhasm: D = 2*ZZ */ | |||
/* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */ | |||
/* asm 2: fe_add(>D=t0,<ZZ=r.X,<ZZ=r.X); */ | |||
FieldOperations.fe_add(out t0, ref r.X, ref r.X); | |||
/* qhasm: X3 = A-B */ | |||
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_sub(out r.X, ref r.Z, ref r.Y); | |||
/* qhasm: Y3 = A+B */ | |||
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_add(out r.Y, ref r.Z, ref r.Y); | |||
/* qhasm: Z3 = D+C */ | |||
/* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_add(>Z3=r.Z,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_add(out r.Z, ref t0, ref r.T); | |||
/* qhasm: T3 = D-C */ | |||
/* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_sub(>T3=r.T,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_sub(out r.T, ref t0, ref r.T); | |||
/* qhasm: return */ | |||
} | |||
} | |||
} |
@@ -0,0 +1,115 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
private static void slide(sbyte[] r, byte[] a) | |||
{ | |||
for (int i = 0; i < 256; ++i) | |||
r[i] = (sbyte)(1 & (a[i >> 3] >> (i & 7))); | |||
for (int i = 0; i < 256; ++i) | |||
{ | |||
if (r[i] != 0) | |||
{ | |||
for (int b = 1; b <= 6 && (i + b) < 256; ++b) | |||
{ | |||
if (r[i + b] != 0) | |||
{ | |||
if (r[i] + (r[i + b] << b) <= 15) | |||
{ | |||
r[i] += (sbyte)(r[i + b] << b); r[i + b] = 0; | |||
} | |||
else if (r[i] - (r[i + b] << b) >= -15) | |||
{ | |||
r[i] -= (sbyte)(r[i + b] << b); | |||
for (int k = i + b; k < 256; ++k) | |||
{ | |||
if (r[k] == 0) | |||
{ | |||
r[k] = 1; | |||
break; | |||
} | |||
r[k] = 0; | |||
} | |||
} | |||
else | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
/* | |||
r = a * A + b * B | |||
where a = a[0]+256*a[1]+...+256^31 a[31]. | |||
and b = b[0]+256*b[1]+...+256^31 b[31]. | |||
B is the Ed25519 base point (x,4/5) with x positive. | |||
*/ | |||
public static void ge_double_scalarmult_vartime(out GroupElementP2 r, byte[] a, ref GroupElementP3 A, byte[] b) | |||
{ | |||
GroupElementPreComp[] Bi = LookupTables.Base2; | |||
// todo: Perhaps remove these allocations? | |||
sbyte[] aslide = new sbyte[256]; | |||
sbyte[] bslide = new sbyte[256]; | |||
GroupElementCached[] Ai = new GroupElementCached[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ | |||
GroupElementP1P1 t; | |||
GroupElementP3 u; | |||
GroupElementP3 A2; | |||
int i; | |||
slide(aslide, a); | |||
slide(bslide, b); | |||
ge_p3_to_cached(out Ai[0], ref A); | |||
ge_p3_dbl(out t, ref A); ge_p1p1_to_p3(out A2, ref t); | |||
ge_add(out t, ref A2, ref Ai[0]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[1], ref u); | |||
ge_add(out t, ref A2, ref Ai[1]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[2], ref u); | |||
ge_add(out t, ref A2, ref Ai[2]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[3], ref u); | |||
ge_add(out t, ref A2, ref Ai[3]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[4], ref u); | |||
ge_add(out t, ref A2, ref Ai[4]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[5], ref u); | |||
ge_add(out t, ref A2, ref Ai[5]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[6], ref u); | |||
ge_add(out t, ref A2, ref Ai[6]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[7], ref u); | |||
ge_p2_0(out r); | |||
for (i = 255; i >= 0; --i) | |||
{ | |||
if ((aslide[i] != 0) || (bslide[i] != 0)) break; | |||
} | |||
for (; i >= 0; --i) | |||
{ | |||
ge_p2_dbl(out t, ref r); | |||
if (aslide[i] > 0) | |||
{ | |||
ge_p1p1_to_p3(out u, ref t); | |||
ge_add(out t, ref u, ref Ai[aslide[i] / 2]); | |||
} | |||
else if (aslide[i] < 0) | |||
{ | |||
ge_p1p1_to_p3(out u, ref t); | |||
ge_sub(out t, ref u, ref Ai[(-aslide[i]) / 2]); | |||
} | |||
if (bslide[i] > 0) | |||
{ | |||
ge_p1p1_to_p3(out u, ref t); | |||
ge_madd(out t, ref u, ref Bi[bslide[i] / 2]); | |||
} | |||
else if (bslide[i] < 0) | |||
{ | |||
ge_p1p1_to_p3(out u, ref t); | |||
ge_msub(out t, ref u, ref Bi[(-bslide[i]) / 2]); | |||
} | |||
ge_p1p1_to_p2(out r, ref t); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
public static int ge_frombytes_negate_vartime(out GroupElementP3 h, byte[] data, int offset) | |||
{ | |||
FieldElement u, v, v3, vxx, check; | |||
FieldOperations.fe_frombytes(out h.Y, data, offset); | |||
FieldOperations.fe_1(out h.Z); | |||
FieldOperations.fe_sq(out u, ref h.Y); | |||
FieldOperations.fe_mul(out v, ref u, ref LookupTables.d); | |||
FieldOperations.fe_sub(out u, ref u, ref h.Z); /* u = y^2-1 */ | |||
FieldOperations.fe_add(out v, ref v, ref h.Z); /* v = dy^2+1 */ | |||
FieldOperations.fe_sq(out v3, ref v); | |||
FieldOperations.fe_mul(out v3, ref v3, ref v); /* v3 = v^3 */ | |||
FieldOperations.fe_sq(out h.X, ref v3); | |||
FieldOperations.fe_mul(out h.X, ref h.X, ref v); | |||
FieldOperations.fe_mul(out h.X, ref h.X, ref u); /* x = uv^7 */ | |||
FieldOperations.fe_pow22523(out h.X, ref h.X); /* x = (uv^7)^((q-5)/8) */ | |||
FieldOperations.fe_mul(out h.X, ref h.X, ref v3); | |||
FieldOperations.fe_mul(out h.X, ref h.X, ref u); /* x = uv^3(uv^7)^((q-5)/8) */ | |||
FieldOperations.fe_sq(out vxx, ref h.X); | |||
FieldOperations.fe_mul(out vxx, ref vxx, ref v); | |||
FieldOperations.fe_sub(out check, ref vxx, ref u); /* vx^2-u */ | |||
if (FieldOperations.fe_isnonzero(ref check) != 0) | |||
{ | |||
FieldOperations.fe_add(out check, ref vxx, ref u); /* vx^2+u */ | |||
if (FieldOperations.fe_isnonzero(ref check) != 0) | |||
{ | |||
h = default(GroupElementP3); | |||
return -1; | |||
} | |||
FieldOperations.fe_mul(out h.X, ref h.X, ref LookupTables.sqrtm1); | |||
} | |||
if (FieldOperations.fe_isnegative(ref h.X) == (data[offset + 31] >> 7)) | |||
FieldOperations.fe_neg(out h.X, ref h.X); | |||
FieldOperations.fe_mul(out h.T, ref h.X, ref h.Y); | |||
return 0; | |||
} | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p + q | |||
*/ | |||
public static void ge_madd(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementPreComp q) | |||
{ | |||
FieldElement t0; | |||
/* qhasm: YpX1 = Y1+X1 */ | |||
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_add(out r.X, ref p.Y, ref p.X); | |||
/* qhasm: YmX1 = Y1-X1 */ | |||
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_sub(out r.Y, ref p.Y, ref p.X); | |||
/* qhasm: A = YpX1*ypx2 */ | |||
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ypx2=fe#15); */ | |||
/* asm 2: fe_mul(>A=r.Z,<YpX1=r.X,<ypx2=q.yplusx); */ | |||
FieldOperations.fe_mul(out r.Z, ref r.X, ref q.yplusx); | |||
/* qhasm: B = YmX1*ymx2 */ | |||
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ymx2=fe#16); */ | |||
/* asm 2: fe_mul(>B=r.Y,<YmX1=r.Y,<ymx2=q.yminusx); */ | |||
FieldOperations.fe_mul(out r.Y, ref r.Y, ref q.yminusx); | |||
/* qhasm: C = xy2d2*T1 */ | |||
/* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */ | |||
/* asm 2: fe_mul(>C=r.T,<xy2d2=q.xy2d,<T1=p.T); */ | |||
FieldOperations.fe_mul(out r.T, ref q.xy2d, ref p.T); | |||
/* qhasm: D = 2*Z1 */ | |||
/* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */ | |||
/* asm 2: fe_add(>D=t0,<Z1=p.Z,<Z1=p.Z); */ | |||
FieldOperations.fe_add(out t0, ref p.Z, ref p.Z); | |||
/* qhasm: X3 = A-B */ | |||
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_sub(out r.X, ref r.Z, ref r.Y); | |||
/* qhasm: Y3 = A+B */ | |||
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_add(out r.Y, ref r.Z, ref r.Y); | |||
/* qhasm: Z3 = D+C */ | |||
/* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_add(>Z3=r.Z,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_add(out r.Z, ref t0, ref r.T); | |||
/* qhasm: T3 = D-C */ | |||
/* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_sub(>T3=r.T,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_sub(out r.T, ref t0, ref r.T); | |||
/* qhasm: return */ | |||
} | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p - q | |||
*/ | |||
public static void ge_msub(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementPreComp q) | |||
{ | |||
FieldElement t0; | |||
/* qhasm: YpX1 = Y1+X1 */ | |||
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_add(out r.X, ref p.Y, ref p.X); | |||
/* qhasm: YmX1 = Y1-X1 */ | |||
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_sub(out r.Y, ref p.Y, ref p.X); | |||
/* qhasm: A = YpX1*ymx2 */ | |||
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ymx2=fe#16); */ | |||
/* asm 2: fe_mul(>A=r.Z,<YpX1=r.X,<ymx2=q.yminusx); */ | |||
FieldOperations.fe_mul(out r.Z, ref r.X, ref q.yminusx); | |||
/* qhasm: B = YmX1*ypx2 */ | |||
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ypx2=fe#15); */ | |||
/* asm 2: fe_mul(>B=r.Y,<YmX1=r.Y,<ypx2=q.yplusx); */ | |||
FieldOperations.fe_mul(out r.Y, ref r.Y, ref q.yplusx); | |||
/* qhasm: C = xy2d2*T1 */ | |||
/* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */ | |||
/* asm 2: fe_mul(>C=r.T,<xy2d2=q.xy2d,<T1=p.T); */ | |||
FieldOperations.fe_mul(out r.T, ref q.xy2d, ref p.T); | |||
/* qhasm: D = 2*Z1 */ | |||
/* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */ | |||
/* asm 2: fe_add(>D=t0,<Z1=p.Z,<Z1=p.Z); */ | |||
FieldOperations.fe_add(out t0, ref p.Z, ref p.Z); | |||
/* qhasm: X3 = A-B */ | |||
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_sub(out r.X, ref r.Z, ref r.Y); | |||
/* qhasm: Y3 = A+B */ | |||
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_add(out r.Y, ref r.Z, ref r.Y); | |||
/* qhasm: Z3 = D-C */ | |||
/* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_sub(>Z3=r.Z,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_sub(out r.Z, ref t0, ref r.T); | |||
/* qhasm: T3 = D+C */ | |||
/* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_add(>T3=r.T,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_add(out r.T, ref t0, ref r.T); | |||
/* qhasm: return */ | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p | |||
*/ | |||
public static void ge_p1p1_to_p2(out GroupElementP2 r, ref GroupElementP1P1 p) | |||
{ | |||
FieldOperations.fe_mul(out r.X, ref p.X, ref p.T); | |||
FieldOperations.fe_mul(out r.Y, ref p.Y, ref p.Z); | |||
FieldOperations.fe_mul(out r.Z, ref p.Z, ref p.T); | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p | |||
*/ | |||
public static void ge_p1p1_to_p3(out GroupElementP3 r, ref GroupElementP1P1 p) | |||
{ | |||
FieldOperations.fe_mul(out r.X, ref p.X, ref p.T); | |||
FieldOperations.fe_mul(out r.Y, ref p.Y, ref p.Z); | |||
FieldOperations.fe_mul(out r.Z, ref p.Z, ref p.T); | |||
FieldOperations.fe_mul(out r.T, ref p.X, ref p.Y); | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
public static void ge_p2_0(out GroupElementP2 h) | |||
{ | |||
FieldOperations.fe_0(out h.X); | |||
FieldOperations.fe_1(out h.Y); | |||
FieldOperations.fe_1(out h.Z); | |||
} | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = 2 * p | |||
*/ | |||
public static void ge_p2_dbl(out GroupElementP1P1 r, ref GroupElementP2 p) | |||
{ | |||
FieldElement t0; | |||
/* qhasm: XX=X1^2 */ | |||
/* asm 1: fe_sq(>XX=fe#1,<X1=fe#11); */ | |||
/* asm 2: fe_sq(>XX=r.X,<X1=p.X); */ | |||
FieldOperations.fe_sq(out r.X, ref p.X); | |||
/* qhasm: YY=Y1^2 */ | |||
/* asm 1: fe_sq(>YY=fe#3,<Y1=fe#12); */ | |||
/* asm 2: fe_sq(>YY=r.Z,<Y1=p.Y); */ | |||
FieldOperations.fe_sq(out r.Z, ref p.Y); | |||
/* qhasm: B=2*Z1^2 */ | |||
/* asm 1: fe_sq2(>B=fe#4,<Z1=fe#13); */ | |||
/* asm 2: fe_sq2(>B=r.T,<Z1=p.Z); */ | |||
FieldOperations.fe_sq2(out r.T, ref p.Z); | |||
/* qhasm: A=X1+Y1 */ | |||
/* asm 1: fe_add(>A=fe#2,<X1=fe#11,<Y1=fe#12); */ | |||
/* asm 2: fe_add(>A=r.Y,<X1=p.X,<Y1=p.Y); */ | |||
FieldOperations.fe_add(out r.Y, ref p.X, ref p.Y); | |||
/* qhasm: AA=A^2 */ | |||
/* asm 1: fe_sq(>AA=fe#5,<A=fe#2); */ | |||
/* asm 2: fe_sq(>AA=t0,<A=r.Y); */ | |||
FieldOperations.fe_sq(out t0, ref r.Y); | |||
/* qhasm: Y3=YY+XX */ | |||
/* asm 1: fe_add(>Y3=fe#2,<YY=fe#3,<XX=fe#1); */ | |||
/* asm 2: fe_add(>Y3=r.Y,<YY=r.Z,<XX=r.X); */ | |||
FieldOperations.fe_add(out r.Y, ref r.Z, ref r.X); | |||
/* qhasm: Z3=YY-XX */ | |||
/* asm 1: fe_sub(>Z3=fe#3,<YY=fe#3,<XX=fe#1); */ | |||
/* asm 2: fe_sub(>Z3=r.Z,<YY=r.Z,<XX=r.X); */ | |||
FieldOperations.fe_sub(out r.Z, ref r.Z, ref r.X); | |||
/* qhasm: X3=AA-Y3 */ | |||
/* asm 1: fe_sub(>X3=fe#1,<AA=fe#5,<Y3=fe#2); */ | |||
/* asm 2: fe_sub(>X3=r.X,<AA=t0,<Y3=r.Y); */ | |||
FieldOperations.fe_sub(out r.X, ref t0, ref r.Y); | |||
/* qhasm: T3=B-Z3 */ | |||
/* asm 1: fe_sub(>T3=fe#4,<B=fe#4,<Z3=fe#3); */ | |||
/* asm 2: fe_sub(>T3=r.T,<B=r.T,<Z3=r.Z); */ | |||
FieldOperations.fe_sub(out r.T, ref r.T, ref r.Z); | |||
/* qhasm: return */ | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
public static void ge_p3_0(out GroupElementP3 h) | |||
{ | |||
FieldOperations.fe_0(out h.X); | |||
FieldOperations.fe_1(out h.Y); | |||
FieldOperations.fe_1(out h.Z); | |||
FieldOperations.fe_0(out h.T); | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = 2 * p | |||
*/ | |||
public static void ge_p3_dbl(out GroupElementP1P1 r, ref GroupElementP3 p) | |||
{ | |||
GroupElementP2 q; | |||
ge_p3_to_p2(out q, ref p); | |||
ge_p2_dbl(out r, ref q); | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p | |||
*/ | |||
public static void ge_p3_to_cached(out GroupElementCached r, ref GroupElementP3 p) | |||
{ | |||
FieldOperations.fe_add(out r.YplusX, ref p.Y, ref p.X); | |||
FieldOperations.fe_sub(out r.YminusX, ref p.Y, ref p.X); | |||
r.Z = p.Z; | |||
FieldOperations.fe_mul(out r.T2d, ref p.T, ref LookupTables.d2); | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p | |||
*/ | |||
public static void ge_p3_to_p2(out GroupElementP2 r, ref GroupElementP3 p) | |||
{ | |||
r.X = p.X; | |||
r.Y = p.Y; | |||
r.Z = p.Z; | |||
} | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
public static void ge_p3_tobytes(byte[] s, int offset, ref GroupElementP3 h) | |||
{ | |||
FieldElement recip; | |||
FieldElement x, y; | |||
FieldOperations.fe_invert(out recip, ref h.Z); | |||
FieldOperations.fe_mul(out x, ref h.X, ref recip); | |||
FieldOperations.fe_mul(out y, ref h.Y, ref recip); | |||
FieldOperations.fe_tobytes(s, offset, ref y); | |||
s[offset + 31] ^= (byte)(FieldOperations.fe_isnegative(ref x) << 7); | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
public static void ge_precomp_0(out GroupElementPreComp h) | |||
{ | |||
FieldOperations.fe_1(out h.yplusx); | |||
FieldOperations.fe_1(out h.yminusx); | |||
FieldOperations.fe_0(out h.xy2d); | |||
} | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
static byte equal(byte b, byte c) | |||
{ | |||
byte ub = b; | |||
byte uc = c; | |||
byte x = (byte)(ub ^ uc); /* 0: yes; 1..255: no */ | |||
uint y = x; /* 0: yes; 1..255: no */ | |||
unchecked { y -= 1; } /* 4294967295: yes; 0..254: no */ | |||
y >>= 31; /* 1: yes; 0: no */ | |||
return (byte)y; | |||
} | |||
static byte negative(sbyte b) | |||
{ | |||
var x = unchecked((ulong)b); /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ | |||
x >>= 63; /* 1: yes; 0: no */ | |||
return (byte)x; | |||
} | |||
static void cmov(ref GroupElementPreComp t, ref GroupElementPreComp u, byte b) | |||
{ | |||
FieldOperations.fe_cmov(ref t.yplusx, ref u.yplusx, b); | |||
FieldOperations.fe_cmov(ref t.yminusx, ref u.yminusx, b); | |||
FieldOperations.fe_cmov(ref t.xy2d, ref u.xy2d, b); | |||
} | |||
static void select(out GroupElementPreComp t, int pos, sbyte b) | |||
{ | |||
GroupElementPreComp minust; | |||
var bnegative = negative(b); | |||
var babs = (byte)(b - (((-bnegative) & b) << 1)); | |||
ge_precomp_0(out t); | |||
var table = LookupTables.Base[pos]; | |||
cmov(ref t, ref table[0], equal(babs, 1)); | |||
cmov(ref t, ref table[1], equal(babs, 2)); | |||
cmov(ref t, ref table[2], equal(babs, 3)); | |||
cmov(ref t, ref table[3], equal(babs, 4)); | |||
cmov(ref t, ref table[4], equal(babs, 5)); | |||
cmov(ref t, ref table[5], equal(babs, 6)); | |||
cmov(ref t, ref table[6], equal(babs, 7)); | |||
cmov(ref t, ref table[7], equal(babs, 8)); | |||
minust.yplusx = t.yminusx; | |||
minust.yminusx = t.yplusx; | |||
FieldOperations.fe_neg(out minust.xy2d, ref t.xy2d); | |||
cmov(ref t, ref minust, bnegative); | |||
} | |||
/* | |||
h = a * B | |||
where a = a[0]+256*a[1]+...+256^31 a[31] | |||
B is the Ed25519 base point (x,4/5) with x positive. | |||
Preconditions: | |||
a[31] <= 127 | |||
*/ | |||
public static void ge_scalarmult_base(out GroupElementP3 h, byte[] a, int offset) | |||
{ | |||
// todo: Perhaps remove this allocation | |||
var e = new sbyte[64]; | |||
sbyte carry; | |||
GroupElementP1P1 r; | |||
GroupElementP2 s; | |||
GroupElementPreComp t; | |||
for (int i = 0; i < 32; ++i) | |||
{ | |||
e[2 * i + 0] = (sbyte)((a[offset + i] >> 0) & 15); | |||
e[2 * i + 1] = (sbyte)((a[offset + i] >> 4) & 15); | |||
} | |||
/* each e[i] is between 0 and 15 */ | |||
/* e[63] is between 0 and 7 */ | |||
carry = 0; | |||
for (int i = 0; i < 63; ++i) | |||
{ | |||
e[i] += carry; | |||
carry = (sbyte)(e[i] + 8); | |||
carry >>= 4; | |||
e[i] -= (sbyte)(carry << 4); | |||
} | |||
e[63] += carry; | |||
/* each e[i] is between -8 and 8 */ | |||
ge_p3_0(out h); | |||
for (int i = 1; i < 64; i += 2) | |||
{ | |||
select(out t, i / 2, e[i]); | |||
ge_madd(out r, ref h, ref t); ge_p1p1_to_p3(out h, ref r); | |||
} | |||
ge_p3_dbl(out r, ref h); ge_p1p1_to_p2(out s, ref r); | |||
ge_p2_dbl(out r, ref s); ge_p1p1_to_p2(out s, ref r); | |||
ge_p2_dbl(out r, ref s); ge_p1p1_to_p2(out s, ref r); | |||
ge_p2_dbl(out r, ref s); ge_p1p1_to_p3(out h, ref r); | |||
for (int i = 0; i < 64; i += 2) | |||
{ | |||
select(out t, i / 2, e[i]); | |||
ge_madd(out r, ref h, ref t); ge_p1p1_to_p3(out h, ref r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
/* | |||
r = p - q | |||
*/ | |||
public static void ge_sub(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementCached q) | |||
{ | |||
FieldElement t0; | |||
/* qhasm: YpX1 = Y1+X1 */ | |||
/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_add(out r.X, ref p.Y, ref p.X); | |||
/* qhasm: YmX1 = Y1-X1 */ | |||
/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ | |||
/* asm 2: fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */ | |||
FieldOperations.fe_sub(out r.Y, ref p.Y, ref p.X); | |||
/* qhasm: A = YpX1*YmX2 */ | |||
/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YmX2=fe#16); */ | |||
/* asm 2: fe_mul(>A=r.Z,<YpX1=r.X,<YmX2=q.YminusX); */ | |||
FieldOperations.fe_mul(out r.Z, ref r.X, ref q.YminusX); | |||
/* qhasm: B = YmX1*YpX2 */ | |||
/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YpX2=fe#15); */ | |||
/* asm 2: fe_mul(>B=r.Y,<YmX1=r.Y,<YpX2=q.YplusX); */ | |||
FieldOperations.fe_mul(out r.Y, ref r.Y, ref q.YplusX); | |||
/* qhasm: C = T2d2*T1 */ | |||
/* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */ | |||
/* asm 2: fe_mul(>C=r.T,<T2d2=q.T2d,<T1=p.T); */ | |||
FieldOperations.fe_mul(out r.T, ref q.T2d, ref p.T); | |||
/* qhasm: ZZ = Z1*Z2 */ | |||
/* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */ | |||
/* asm 2: fe_mul(>ZZ=r.X,<Z1=p.Z,<Z2=q.Z); */ | |||
FieldOperations.fe_mul(out r.X, ref p.Z, ref q.Z); | |||
/* qhasm: D = 2*ZZ */ | |||
/* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */ | |||
/* asm 2: fe_add(>D=t0,<ZZ=r.X,<ZZ=r.X); */ | |||
FieldOperations.fe_add(out t0, ref r.X, ref r.X); | |||
/* qhasm: X3 = A-B */ | |||
/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_sub(out r.X, ref r.Z, ref r.Y); | |||
/* qhasm: Y3 = A+B */ | |||
/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ | |||
/* asm 2: fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */ | |||
FieldOperations.fe_add(out r.Y, ref r.Z, ref r.Y); | |||
/* qhasm: Z3 = D-C */ | |||
/* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_sub(>Z3=r.Z,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_sub(out r.Z, ref t0, ref r.T); | |||
/* qhasm: T3 = D+C */ | |||
/* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */ | |||
/* asm 2: fe_add(>T3=r.T,<D=t0,<C=r.T); */ | |||
FieldOperations.fe_add(out r.T, ref t0, ref r.T); | |||
/* qhasm: return */ | |||
} | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class GroupOperations | |||
{ | |||
public static void ge_tobytes(byte[] s, int offset, ref GroupElementP2 h) | |||
{ | |||
FieldElement recip; | |||
FieldElement x, y; | |||
FieldOperations.fe_invert(out recip, ref h.Z); | |||
FieldOperations.fe_mul(out x, ref h.X, ref recip); | |||
FieldOperations.fe_mul(out y, ref h.Y, ref recip); | |||
FieldOperations.fe_tobytes(s, offset, ref y); | |||
s[offset + 31] ^= (byte)(FieldOperations.fe_isnegative(ref x) << 7); | |||
} | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class Ed25519Operations | |||
{ | |||
public static void crypto_sign_keypair(byte[] pk, int pkoffset, byte[] sk, int skoffset, byte[] seed, int seedoffset) | |||
{ | |||
GroupElementP3 A; | |||
int i; | |||
Array.Copy(seed, seedoffset, sk, skoffset, 32); | |||
byte[] h = Sha512.Hash(sk, skoffset, 32);//ToDo: Remove alloc | |||
ScalarOperations.sc_clamp(h, 0); | |||
GroupOperations.ge_scalarmult_base(out A, h, 0); | |||
GroupOperations.ge_p3_tobytes(pk, pkoffset, ref A); | |||
for (i = 0; i < 32; ++i) sk[skoffset + 32 + i] = pk[pkoffset + i]; | |||
CryptoBytes.Wipe(h); | |||
} | |||
} | |||
} |
@@ -0,0 +1,80 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class Ed25519Operations | |||
{ | |||
// Original crypto_sign_open, for reference only | |||
/*public static int crypto_sign_open( | |||
byte[] m, out int mlen, | |||
byte[] sm, int smlen, | |||
byte[] pk) | |||
{ | |||
byte[] h = new byte[64]; | |||
byte[] checkr = new byte[32]; | |||
GroupElementP3 A; | |||
GroupElementP2 R; | |||
int i; | |||
mlen = -1; | |||
if (smlen < 64) return -1; | |||
if ((sm[63] & 224) != 0) return -1; | |||
if (GroupOperations.ge_frombytes_negate_vartime(out A, pk, 0) != 0) return -1; | |||
for (i = 0; i < smlen; ++i) m[i] = sm[i]; | |||
for (i = 0; i < 32; ++i) m[32 + i] = pk[i]; | |||
Sha512BclWrapper.crypto_hash_sha512(h, m, 0, smlen); | |||
ScalarOperations.sc_reduce(h); | |||
var sm32 = new byte[32]; | |||
Array.Copy(sm, 32, sm32, 0, 32); | |||
GroupOperations.ge_double_scalarmult_vartime(out R, h, ref A, sm32); | |||
GroupOperations.ge_tobytes(checkr, 0, ref R); | |||
if (Helpers.crypto_verify_32(checkr, sm) != 0) | |||
{ | |||
for (i = 0; i < smlen; ++i) | |||
m[i] = 0; | |||
return -1; | |||
} | |||
for (i = 0; i < smlen - 64; ++i) | |||
m[i] = sm[64 + i]; | |||
for (i = smlen - 64; i < smlen; ++i) | |||
m[i] = 0; | |||
mlen = smlen - 64; | |||
return 0; | |||
}*/ | |||
public static bool crypto_sign_verify( | |||
byte[] sig, int sigoffset, | |||
byte[] m, int moffset, int mlen, | |||
byte[] pk, int pkoffset) | |||
{ | |||
byte[] h; | |||
byte[] checkr = new byte[32]; | |||
GroupElementP3 A; | |||
GroupElementP2 R; | |||
if ((sig[sigoffset + 63] & 224) != 0) return false; | |||
if (GroupOperations.ge_frombytes_negate_vartime(out A, pk, pkoffset) != 0) | |||
return false; | |||
var hasher = new Sha512(); | |||
hasher.Update(sig, sigoffset, 32); | |||
hasher.Update(pk, pkoffset, 32); | |||
hasher.Update(m, moffset, mlen); | |||
h = hasher.Finalize(); | |||
ScalarOperations.sc_reduce(h); | |||
var sm32 = new byte[32];//todo: remove allocation | |||
Array.Copy(sig, sigoffset + 32, sm32, 0, 32); | |||
GroupOperations.ge_double_scalarmult_vartime(out R, h, ref A, sm32); | |||
GroupOperations.ge_tobytes(checkr, 0, ref R); | |||
var result = CryptoBytes.ConstantTimeEquals(checkr, 0, sig, sigoffset, 32); | |||
CryptoBytes.Wipe(h); | |||
CryptoBytes.Wipe(checkr); | |||
return result; | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class ScalarOperations | |||
{ | |||
public static void sc_clamp(byte[] s, int offset) | |||
{ | |||
s[offset + 0] &= 248; | |||
s[offset + 31] &= 127; | |||
s[offset + 31] |= 64; | |||
} | |||
} | |||
} |
@@ -0,0 +1,374 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class ScalarOperations | |||
{ | |||
static long load_3(byte[] input, int offset) | |||
{ | |||
long result; | |||
result = (long)input[offset + 0]; | |||
result |= ((long)input[offset + 1]) << 8; | |||
result |= ((long)input[offset + 2]) << 16; | |||
return result; | |||
} | |||
static long load_4(byte[] input, int offset) | |||
{ | |||
long result; | |||
result = (long)input[offset + 0]; | |||
result |= ((long)input[offset + 1]) << 8; | |||
result |= ((long)input[offset + 2]) << 16; | |||
result |= ((long)input[offset + 3]) << 24; | |||
return result; | |||
} | |||
/* | |||
Input: | |||
a[0]+256*a[1]+...+256^31*a[31] = a | |||
b[0]+256*b[1]+...+256^31*b[31] = b | |||
c[0]+256*c[1]+...+256^31*c[31] = c | |||
Output: | |||
s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l | |||
where l = 2^252 + 27742317777372353535851937790883648493. | |||
*/ | |||
public static void sc_muladd(byte[] s, byte[] a, byte[] b, byte[] c) | |||
{ | |||
long a0 = 2097151 & load_3(a, 0); | |||
long a1 = 2097151 & (load_4(a, 2) >> 5); | |||
long a2 = 2097151 & (load_3(a, 5) >> 2); | |||
long a3 = 2097151 & (load_4(a, 7) >> 7); | |||
long a4 = 2097151 & (load_4(a, 10) >> 4); | |||
long a5 = 2097151 & (load_3(a, 13) >> 1); | |||
long a6 = 2097151 & (load_4(a, 15) >> 6); | |||
long a7 = 2097151 & (load_3(a, 18) >> 3); | |||
long a8 = 2097151 & load_3(a, 21); | |||
long a9 = 2097151 & (load_4(a, 23) >> 5); | |||
long a10 = 2097151 & (load_3(a, 26) >> 2); | |||
long a11 = (load_4(a, 28) >> 7); | |||
long b0 = 2097151 & load_3(b, 0); | |||
long b1 = 2097151 & (load_4(b, 2) >> 5); | |||
long b2 = 2097151 & (load_3(b, 5) >> 2); | |||
long b3 = 2097151 & (load_4(b, 7) >> 7); | |||
long b4 = 2097151 & (load_4(b, 10) >> 4); | |||
long b5 = 2097151 & (load_3(b, 13) >> 1); | |||
long b6 = 2097151 & (load_4(b, 15) >> 6); | |||
long b7 = 2097151 & (load_3(b, 18) >> 3); | |||
long b8 = 2097151 & load_3(b, 21); | |||
long b9 = 2097151 & (load_4(b, 23) >> 5); | |||
long b10 = 2097151 & (load_3(b, 26) >> 2); | |||
long b11 = (load_4(b, 28) >> 7); | |||
long c0 = 2097151 & load_3(c, 0); | |||
long c1 = 2097151 & (load_4(c, 2) >> 5); | |||
long c2 = 2097151 & (load_3(c, 5) >> 2); | |||
long c3 = 2097151 & (load_4(c, 7) >> 7); | |||
long c4 = 2097151 & (load_4(c, 10) >> 4); | |||
long c5 = 2097151 & (load_3(c, 13) >> 1); | |||
long c6 = 2097151 & (load_4(c, 15) >> 6); | |||
long c7 = 2097151 & (load_3(c, 18) >> 3); | |||
long c8 = 2097151 & load_3(c, 21); | |||
long c9 = 2097151 & (load_4(c, 23) >> 5); | |||
long c10 = 2097151 & (load_3(c, 26) >> 2); | |||
long c11 = (load_4(c, 28) >> 7); | |||
long s0; | |||
long s1; | |||
long s2; | |||
long s3; | |||
long s4; | |||
long s5; | |||
long s6; | |||
long s7; | |||
long s8; | |||
long s9; | |||
long s10; | |||
long s11; | |||
long s12; | |||
long s13; | |||
long s14; | |||
long s15; | |||
long s16; | |||
long s17; | |||
long s18; | |||
long s19; | |||
long s20; | |||
long s21; | |||
long s22; | |||
long s23; | |||
long carry0; | |||
long carry1; | |||
long carry2; | |||
long carry3; | |||
long carry4; | |||
long carry5; | |||
long carry6; | |||
long carry7; | |||
long carry8; | |||
long carry9; | |||
long carry10; | |||
long carry11; | |||
long carry12; | |||
long carry13; | |||
long carry14; | |||
long carry15; | |||
long carry16; | |||
long carry17; | |||
long carry18; | |||
long carry19; | |||
long carry20; | |||
long carry21; | |||
long carry22; | |||
s0 = c0 + a0 * b0; | |||
s1 = c1 + a0 * b1 + a1 * b0; | |||
s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; | |||
s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; | |||
s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; | |||
s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; | |||
s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; | |||
s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; | |||
s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; | |||
s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; | |||
s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; | |||
s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; | |||
s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; | |||
s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; | |||
s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; | |||
s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; | |||
s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; | |||
s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; | |||
s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; | |||
s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; | |||
s20 = a9 * b11 + a10 * b10 + a11 * b9; | |||
s21 = a10 * b11 + a11 * b10; | |||
s22 = a11 * b11; | |||
s23 = 0; | |||
carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; | |||
carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; | |||
carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; | |||
carry18 = (s18 + (1 << 20)) >> 21; s19 += carry18; s18 -= carry18 << 21; | |||
carry20 = (s20 + (1 << 20)) >> 21; s21 += carry20; s20 -= carry20 << 21; | |||
carry22 = (s22 + (1 << 20)) >> 21; s23 += carry22; s22 -= carry22 << 21; | |||
carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; | |||
carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; | |||
carry17 = (s17 + (1 << 20)) >> 21; s18 += carry17; s17 -= carry17 << 21; | |||
carry19 = (s19 + (1 << 20)) >> 21; s20 += carry19; s19 -= carry19 << 21; | |||
carry21 = (s21 + (1 << 20)) >> 21; s22 += carry21; s21 -= carry21 << 21; | |||
s11 += s23 * 666643; | |||
s12 += s23 * 470296; | |||
s13 += s23 * 654183; | |||
s14 -= s23 * 997805; | |||
s15 += s23 * 136657; | |||
s16 -= s23 * 683901; | |||
s23 = 0; | |||
s10 += s22 * 666643; | |||
s11 += s22 * 470296; | |||
s12 += s22 * 654183; | |||
s13 -= s22 * 997805; | |||
s14 += s22 * 136657; | |||
s15 -= s22 * 683901; | |||
s22 = 0; | |||
s9 += s21 * 666643; | |||
s10 += s21 * 470296; | |||
s11 += s21 * 654183; | |||
s12 -= s21 * 997805; | |||
s13 += s21 * 136657; | |||
s14 -= s21 * 683901; | |||
s21 = 0; | |||
s8 += s20 * 666643; | |||
s9 += s20 * 470296; | |||
s10 += s20 * 654183; | |||
s11 -= s20 * 997805; | |||
s12 += s20 * 136657; | |||
s13 -= s20 * 683901; | |||
s20 = 0; | |||
s7 += s19 * 666643; | |||
s8 += s19 * 470296; | |||
s9 += s19 * 654183; | |||
s10 -= s19 * 997805; | |||
s11 += s19 * 136657; | |||
s12 -= s19 * 683901; | |||
s19 = 0; | |||
s6 += s18 * 666643; | |||
s7 += s18 * 470296; | |||
s8 += s18 * 654183; | |||
s9 -= s18 * 997805; | |||
s10 += s18 * 136657; | |||
s11 -= s18 * 683901; | |||
s18 = 0; | |||
carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; | |||
carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; | |||
carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; | |||
carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; | |||
carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; | |||
s5 += s17 * 666643; | |||
s6 += s17 * 470296; | |||
s7 += s17 * 654183; | |||
s8 -= s17 * 997805; | |||
s9 += s17 * 136657; | |||
s10 -= s17 * 683901; | |||
s17 = 0; | |||
s4 += s16 * 666643; | |||
s5 += s16 * 470296; | |||
s6 += s16 * 654183; | |||
s7 -= s16 * 997805; | |||
s8 += s16 * 136657; | |||
s9 -= s16 * 683901; | |||
s16 = 0; | |||
s3 += s15 * 666643; | |||
s4 += s15 * 470296; | |||
s5 += s15 * 654183; | |||
s6 -= s15 * 997805; | |||
s7 += s15 * 136657; | |||
s8 -= s15 * 683901; | |||
s15 = 0; | |||
s2 += s14 * 666643; | |||
s3 += s14 * 470296; | |||
s4 += s14 * 654183; | |||
s5 -= s14 * 997805; | |||
s6 += s14 * 136657; | |||
s7 -= s14 * 683901; | |||
s14 = 0; | |||
s1 += s13 * 666643; | |||
s2 += s13 * 470296; | |||
s3 += s13 * 654183; | |||
s4 -= s13 * 997805; | |||
s5 += s13 * 136657; | |||
s6 -= s13 * 683901; | |||
s13 = 0; | |||
s0 += s12 * 666643; | |||
s1 += s12 * 470296; | |||
s2 += s12 * 654183; | |||
s3 -= s12 * 997805; | |||
s4 += s12 * 136657; | |||
s5 -= s12 * 683901; | |||
s12 = 0; | |||
carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
s0 += s12 * 666643; | |||
s1 += s12 * 470296; | |||
s2 += s12 * 654183; | |||
s3 -= s12 * 997805; | |||
s4 += s12 * 136657; | |||
s5 -= s12 * 683901; | |||
s12 = 0; | |||
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
s0 += s12 * 666643; | |||
s1 += s12 * 470296; | |||
s2 += s12 * 654183; | |||
s3 -= s12 * 997805; | |||
s4 += s12 * 136657; | |||
s5 -= s12 * 683901; | |||
s12 = 0; | |||
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
unchecked | |||
{ | |||
s[0] = (byte)(s0 >> 0); | |||
s[1] = (byte)(s0 >> 8); | |||
s[2] = (byte)((s0 >> 16) | (s1 << 5)); | |||
s[3] = (byte)(s1 >> 3); | |||
s[4] = (byte)(s1 >> 11); | |||
s[5] = (byte)((s1 >> 19) | (s2 << 2)); | |||
s[6] = (byte)(s2 >> 6); | |||
s[7] = (byte)((s2 >> 14) | (s3 << 7)); | |||
s[8] = (byte)(s3 >> 1); | |||
s[9] = (byte)(s3 >> 9); | |||
s[10] = (byte)((s3 >> 17) | (s4 << 4)); | |||
s[11] = (byte)(s4 >> 4); | |||
s[12] = (byte)(s4 >> 12); | |||
s[13] = (byte)((s4 >> 20) | (s5 << 1)); | |||
s[14] = (byte)(s5 >> 7); | |||
s[15] = (byte)((s5 >> 15) | (s6 << 6)); | |||
s[16] = (byte)(s6 >> 2); | |||
s[17] = (byte)(s6 >> 10); | |||
s[18] = (byte)((s6 >> 18) | (s7 << 3)); | |||
s[19] = (byte)(s7 >> 5); | |||
s[20] = (byte)(s7 >> 13); | |||
s[21] = (byte)(s8 >> 0); | |||
s[22] = (byte)(s8 >> 8); | |||
s[23] = (byte)((s8 >> 16) | (s9 << 5)); | |||
s[24] = (byte)(s9 >> 3); | |||
s[25] = (byte)(s9 >> 11); | |||
s[26] = (byte)((s9 >> 19) | (s10 << 2)); | |||
s[27] = (byte)(s10 >> 6); | |||
s[28] = (byte)((s10 >> 14) | (s11 << 7)); | |||
s[29] = (byte)(s11 >> 1); | |||
s[30] = (byte)(s11 >> 9); | |||
s[31] = (byte)(s11 >> 17); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,264 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class ScalarOperations | |||
{ | |||
/* | |||
Input: | |||
s[0]+256*s[1]+...+256^63*s[63] = s | |||
Output: | |||
s[0]+256*s[1]+...+256^31*s[31] = s mod l | |||
where l = 2^252 + 27742317777372353535851937790883648493. | |||
Overwrites s in place. | |||
*/ | |||
public static void sc_reduce(byte[] s) | |||
{ | |||
long s0 = 2097151 & load_3(s, 0); | |||
long s1 = 2097151 & (load_4(s, 2) >> 5); | |||
long s2 = 2097151 & (load_3(s, 5) >> 2); | |||
long s3 = 2097151 & (load_4(s, 7) >> 7); | |||
long s4 = 2097151 & (load_4(s, 10) >> 4); | |||
long s5 = 2097151 & (load_3(s, 13) >> 1); | |||
long s6 = 2097151 & (load_4(s, 15) >> 6); | |||
long s7 = 2097151 & (load_3(s, 18) >> 3); | |||
long s8 = 2097151 & load_3(s, 21); | |||
long s9 = 2097151 & (load_4(s, 23) >> 5); | |||
long s10 = 2097151 & (load_3(s, 26) >> 2); | |||
long s11 = 2097151 & (load_4(s, 28) >> 7); | |||
long s12 = 2097151 & (load_4(s, 31) >> 4); | |||
long s13 = 2097151 & (load_3(s, 34) >> 1); | |||
long s14 = 2097151 & (load_4(s, 36) >> 6); | |||
long s15 = 2097151 & (load_3(s, 39) >> 3); | |||
long s16 = 2097151 & load_3(s, 42); | |||
long s17 = 2097151 & (load_4(s, 44) >> 5); | |||
long s18 = 2097151 & (load_3(s, 47) >> 2); | |||
long s19 = 2097151 & (load_4(s, 49) >> 7); | |||
long s20 = 2097151 & (load_4(s, 52) >> 4); | |||
long s21 = 2097151 & (load_3(s, 55) >> 1); | |||
long s22 = 2097151 & (load_4(s, 57) >> 6); | |||
long s23 = (load_4(s, 60) >> 3); | |||
long carry0; | |||
long carry1; | |||
long carry2; | |||
long carry3; | |||
long carry4; | |||
long carry5; | |||
long carry6; | |||
long carry7; | |||
long carry8; | |||
long carry9; | |||
long carry10; | |||
long carry11; | |||
long carry12; | |||
long carry13; | |||
long carry14; | |||
long carry15; | |||
long carry16; | |||
s11 += s23 * 666643; | |||
s12 += s23 * 470296; | |||
s13 += s23 * 654183; | |||
s14 -= s23 * 997805; | |||
s15 += s23 * 136657; | |||
s16 -= s23 * 683901; | |||
s23 = 0; | |||
s10 += s22 * 666643; | |||
s11 += s22 * 470296; | |||
s12 += s22 * 654183; | |||
s13 -= s22 * 997805; | |||
s14 += s22 * 136657; | |||
s15 -= s22 * 683901; | |||
s22 = 0; | |||
s9 += s21 * 666643; | |||
s10 += s21 * 470296; | |||
s11 += s21 * 654183; | |||
s12 -= s21 * 997805; | |||
s13 += s21 * 136657; | |||
s14 -= s21 * 683901; | |||
s21 = 0; | |||
s8 += s20 * 666643; | |||
s9 += s20 * 470296; | |||
s10 += s20 * 654183; | |||
s11 -= s20 * 997805; | |||
s12 += s20 * 136657; | |||
s13 -= s20 * 683901; | |||
s20 = 0; | |||
s7 += s19 * 666643; | |||
s8 += s19 * 470296; | |||
s9 += s19 * 654183; | |||
s10 -= s19 * 997805; | |||
s11 += s19 * 136657; | |||
s12 -= s19 * 683901; | |||
s19 = 0; | |||
s6 += s18 * 666643; | |||
s7 += s18 * 470296; | |||
s8 += s18 * 654183; | |||
s9 -= s18 * 997805; | |||
s10 += s18 * 136657; | |||
s11 -= s18 * 683901; | |||
s18 = 0; | |||
carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; | |||
carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; | |||
carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; | |||
carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; | |||
carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; | |||
s5 += s17 * 666643; | |||
s6 += s17 * 470296; | |||
s7 += s17 * 654183; | |||
s8 -= s17 * 997805; | |||
s9 += s17 * 136657; | |||
s10 -= s17 * 683901; | |||
s17 = 0; | |||
s4 += s16 * 666643; | |||
s5 += s16 * 470296; | |||
s6 += s16 * 654183; | |||
s7 -= s16 * 997805; | |||
s8 += s16 * 136657; | |||
s9 -= s16 * 683901; | |||
s16 = 0; | |||
s3 += s15 * 666643; | |||
s4 += s15 * 470296; | |||
s5 += s15 * 654183; | |||
s6 -= s15 * 997805; | |||
s7 += s15 * 136657; | |||
s8 -= s15 * 683901; | |||
s15 = 0; | |||
s2 += s14 * 666643; | |||
s3 += s14 * 470296; | |||
s4 += s14 * 654183; | |||
s5 -= s14 * 997805; | |||
s6 += s14 * 136657; | |||
s7 -= s14 * 683901; | |||
s14 = 0; | |||
s1 += s13 * 666643; | |||
s2 += s13 * 470296; | |||
s3 += s13 * 654183; | |||
s4 -= s13 * 997805; | |||
s5 += s13 * 136657; | |||
s6 -= s13 * 683901; | |||
s13 = 0; | |||
s0 += s12 * 666643; | |||
s1 += s12 * 470296; | |||
s2 += s12 * 654183; | |||
s3 -= s12 * 997805; | |||
s4 += s12 * 136657; | |||
s5 -= s12 * 683901; | |||
s12 = 0; | |||
carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
s0 += s12 * 666643; | |||
s1 += s12 * 470296; | |||
s2 += s12 * 654183; | |||
s3 -= s12 * 997805; | |||
s4 += s12 * 136657; | |||
s5 -= s12 * 683901; | |||
s12 = 0; | |||
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; | |||
s0 += s12 * 666643; | |||
s1 += s12 * 470296; | |||
s2 += s12 * 654183; | |||
s3 -= s12 * 997805; | |||
s4 += s12 * 136657; | |||
s5 -= s12 * 683901; | |||
s12 = 0; | |||
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; | |||
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; | |||
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; | |||
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; | |||
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; | |||
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; | |||
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; | |||
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; | |||
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; | |||
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; | |||
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; | |||
unchecked | |||
{ | |||
s[0] = (byte)(s0 >> 0); | |||
s[1] = (byte)(s0 >> 8); | |||
s[2] = (byte)((s0 >> 16) | (s1 << 5)); | |||
s[3] = (byte)(s1 >> 3); | |||
s[4] = (byte)(s1 >> 11); | |||
s[5] = (byte)((s1 >> 19) | (s2 << 2)); | |||
s[6] = (byte)(s2 >> 6); | |||
s[7] = (byte)((s2 >> 14) | (s3 << 7)); | |||
s[8] = (byte)(s3 >> 1); | |||
s[9] = (byte)(s3 >> 9); | |||
s[10] = (byte)((s3 >> 17) | (s4 << 4)); | |||
s[11] = (byte)(s4 >> 4); | |||
s[12] = (byte)(s4 >> 12); | |||
s[13] = (byte)((s4 >> 20) | (s5 << 1)); | |||
s[14] = (byte)(s5 >> 7); | |||
s[15] = (byte)((s5 >> 15) | (s6 << 6)); | |||
s[16] = (byte)(s6 >> 2); | |||
s[17] = (byte)(s6 >> 10); | |||
s[18] = (byte)((s6 >> 18) | (s7 << 3)); | |||
s[19] = (byte)(s7 >> 5); | |||
s[20] = (byte)(s7 >> 13); | |||
s[21] = (byte)(s8 >> 0); | |||
s[22] = (byte)(s8 >> 8); | |||
s[23] = (byte)((s8 >> 16) | (s9 << 5)); | |||
s[24] = (byte)(s9 >> 3); | |||
s[25] = (byte)(s9 >> 11); | |||
s[26] = (byte)((s9 >> 19) | (s10 << 2)); | |||
s[27] = (byte)(s10 >> 6); | |||
s[28] = (byte)((s10 >> 14) | (s11 << 7)); | |||
s[29] = (byte)(s11 >> 1); | |||
s[30] = (byte)(s11 >> 9); | |||
s[31] = (byte)(s11 >> 17); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,153 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
public static class MontgomeryOperations | |||
{ | |||
public static void scalarmult( | |||
byte[] q, int qoffset, | |||
byte[] n, int noffset, | |||
byte[] p, int poffset) | |||
{ | |||
FieldElement p0, q0; | |||
FieldOperations.fe_frombytes2(out p0, p, poffset); | |||
scalarmult(out q0, n, noffset, ref p0); | |||
FieldOperations.fe_tobytes(q, qoffset, ref q0); | |||
} | |||
internal static void scalarmult( | |||
out FieldElement q, | |||
byte[] n, int noffset, | |||
ref FieldElement p) | |||
{ | |||
byte[] e = new byte[32];//ToDo: remove allocation | |||
FieldElement x1, x2, x3; | |||
FieldElement z2, z3; | |||
FieldElement tmp0, tmp1; | |||
for (int i = 0; i < 32; ++i) | |||
e[i] = n[noffset + i]; | |||
ScalarOperations.sc_clamp(e, 0); | |||
x1 = p; | |||
FieldOperations.fe_1(out x2); | |||
FieldOperations.fe_0(out z2); | |||
x3 = x1; | |||
FieldOperations.fe_1(out z3); | |||
uint swap = 0; | |||
for (int pos = 254; pos >= 0; --pos) | |||
{ | |||
uint b = (uint)(e[pos / 8] >> (pos & 7)); | |||
b &= 1; | |||
swap ^= b; | |||
FieldOperations.fe_cswap(ref x2, ref x3, swap); | |||
FieldOperations.fe_cswap(ref z2, ref z3, swap); | |||
swap = b; | |||
/* qhasm: enter ladder */ | |||
/* qhasm: D = X3-Z3 */ | |||
/* asm 1: fe_sub(>D=fe#5,<X3=fe#3,<Z3=fe#4); */ | |||
/* asm 2: fe_sub(>D=tmp0,<X3=x3,<Z3=z3); */ | |||
FieldOperations.fe_sub(out tmp0, ref x3, ref z3); | |||
/* qhasm: B = X2-Z2 */ | |||
/* asm 1: fe_sub(>B=fe#6,<X2=fe#1,<Z2=fe#2); */ | |||
/* asm 2: fe_sub(>B=tmp1,<X2=x2,<Z2=z2); */ | |||
FieldOperations.fe_sub(out tmp1, ref x2, ref z2); | |||
/* qhasm: A = X2+Z2 */ | |||
/* asm 1: fe_add(>A=fe#1,<X2=fe#1,<Z2=fe#2); */ | |||
/* asm 2: fe_add(>A=x2,<X2=x2,<Z2=z2); */ | |||
FieldOperations.fe_add(out x2, ref x2, ref z2); | |||
/* qhasm: C = X3+Z3 */ | |||
/* asm 1: fe_add(>C=fe#2,<X3=fe#3,<Z3=fe#4); */ | |||
/* asm 2: fe_add(>C=z2,<X3=x3,<Z3=z3); */ | |||
FieldOperations.fe_add(out z2, ref x3, ref z3); | |||
/* qhasm: DA = D*A */ | |||
/* asm 1: fe_mul(>DA=fe#4,<D=fe#5,<A=fe#1); */ | |||
/* asm 2: fe_mul(>DA=z3,<D=tmp0,<A=x2); */ | |||
FieldOperations.fe_mul(out z3, ref tmp0, ref x2); | |||
/* qhasm: CB = C*B */ | |||
/* asm 1: fe_mul(>CB=fe#2,<C=fe#2,<B=fe#6); */ | |||
/* asm 2: fe_mul(>CB=z2,<C=z2,<B=tmp1); */ | |||
FieldOperations.fe_mul(out z2, ref z2, ref tmp1); | |||
/* qhasm: BB = B^2 */ | |||
/* asm 1: fe_sq(>BB=fe#5,<B=fe#6); */ | |||
/* asm 2: fe_sq(>BB=tmp0,<B=tmp1); */ | |||
FieldOperations.fe_sq(out tmp0, ref tmp1); | |||
/* qhasm: AA = A^2 */ | |||
/* asm 1: fe_sq(>AA=fe#6,<A=fe#1); */ | |||
/* asm 2: fe_sq(>AA=tmp1,<A=x2); */ | |||
FieldOperations.fe_sq(out tmp1, ref x2); | |||
/* qhasm: t0 = DA+CB */ | |||
/* asm 1: fe_add(>t0=fe#3,<DA=fe#4,<CB=fe#2); */ | |||
/* asm 2: fe_add(>t0=x3,<DA=z3,<CB=z2); */ | |||
FieldOperations.fe_add(out x3, ref z3, ref z2); | |||
/* qhasm: assign x3 to t0 */ | |||
/* qhasm: t1 = DA-CB */ | |||
/* asm 1: fe_sub(>t1=fe#2,<DA=fe#4,<CB=fe#2); */ | |||
/* asm 2: fe_sub(>t1=z2,<DA=z3,<CB=z2); */ | |||
FieldOperations.fe_sub(out z2, ref z3, ref z2); | |||
/* qhasm: X4 = AA*BB */ | |||
/* asm 1: fe_mul(>X4=fe#1,<AA=fe#6,<BB=fe#5); */ | |||
/* asm 2: fe_mul(>X4=x2,<AA=tmp1,<BB=tmp0); */ | |||
FieldOperations.fe_mul(out x2, ref tmp1, ref tmp0); | |||
/* qhasm: E = AA-BB */ | |||
/* asm 1: fe_sub(>E=fe#6,<AA=fe#6,<BB=fe#5); */ | |||
/* asm 2: fe_sub(>E=tmp1,<AA=tmp1,<BB=tmp0); */ | |||
FieldOperations.fe_sub(out tmp1, ref tmp1, ref tmp0); | |||
/* qhasm: t2 = t1^2 */ | |||
/* asm 1: fe_sq(>t2=fe#2,<t1=fe#2); */ | |||
/* asm 2: fe_sq(>t2=z2,<t1=z2); */ | |||
FieldOperations.fe_sq(out z2, ref z2); | |||
/* qhasm: t3 = a24*E */ | |||
/* asm 1: fe_mul121666(>t3=fe#4,<E=fe#6); */ | |||
/* asm 2: fe_mul121666(>t3=z3,<E=tmp1); */ | |||
FieldOperations.fe_mul121666(out z3, ref tmp1); | |||
/* qhasm: X5 = t0^2 */ | |||
/* asm 1: fe_sq(>X5=fe#3,<t0=fe#3); */ | |||
/* asm 2: fe_sq(>X5=x3,<t0=x3); */ | |||
FieldOperations.fe_sq(out x3, ref x3); | |||
/* qhasm: t4 = BB+t3 */ | |||
/* asm 1: fe_add(>t4=fe#5,<BB=fe#5,<t3=fe#4); */ | |||
/* asm 2: fe_add(>t4=tmp0,<BB=tmp0,<t3=z3); */ | |||
FieldOperations.fe_add(out tmp0, ref tmp0, ref z3); | |||
/* qhasm: Z5 = X1*t2 */ | |||
/* asm 1: fe_mul(>Z5=fe#4,x1,<t2=fe#2); */ | |||
/* asm 2: fe_mul(>Z5=z3,x1,<t2=z2); */ | |||
FieldOperations.fe_mul(out z3, ref x1, ref z2); | |||
/* qhasm: Z4 = E*t4 */ | |||
/* asm 1: fe_mul(>Z4=fe#2,<E=fe#6,<t4=fe#5); */ | |||
/* asm 2: fe_mul(>Z4=z2,<E=tmp1,<t4=tmp0); */ | |||
FieldOperations.fe_mul(out z2, ref tmp1, ref tmp0); | |||
/* qhasm: return */ | |||
} | |||
FieldOperations.fe_cswap(ref x2, ref x3, swap); | |||
FieldOperations.fe_cswap(ref z2, ref z3, swap); | |||
FieldOperations.fe_invert(out z2, ref z2); | |||
FieldOperations.fe_mul(out x2, ref x2, ref z2); | |||
q = x2; | |||
CryptoBytes.Wipe(e); | |||
} | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class Ed25519Operations | |||
{ | |||
public static void crypto_sign( | |||
byte[] sig, int sigoffset, | |||
byte[] m, int moffset, int mlen, | |||
byte[] sk, int skoffset) | |||
{ | |||
byte[] az, r, hram; | |||
GroupElementP3 R; | |||
var hasher = new Sha512(); | |||
{ | |||
hasher.Update(sk, skoffset, 32); | |||
az = hasher.Finalize(); | |||
ScalarOperations.sc_clamp(az, 0); | |||
hasher.Init(); | |||
hasher.Update(az, 32, 32); | |||
hasher.Update(m, moffset, mlen); | |||
r = hasher.Finalize(); | |||
ScalarOperations.sc_reduce(r); | |||
GroupOperations.ge_scalarmult_base(out R, r, 0); | |||
GroupOperations.ge_p3_tobytes(sig, sigoffset, ref R); | |||
hasher.Init(); | |||
hasher.Update(sig, sigoffset, 32); | |||
hasher.Update(sk, skoffset + 32, 32); | |||
hasher.Update(m, moffset, mlen); | |||
hram = hasher.Finalize(); | |||
ScalarOperations.sc_reduce(hram); | |||
var s = new byte[32];//todo: remove allocation | |||
Array.Copy(sig, sigoffset + 32, s, 0, 32); | |||
ScalarOperations.sc_muladd(s, hram, az, r); | |||
Array.Copy(s, 0, sig, sigoffset + 32, 32); | |||
CryptoBytes.Wipe(s); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Discord.Net.ED25519.Ed25519Ref10 | |||
{ | |||
internal static partial class LookupTables | |||
{ | |||
internal static FieldElement sqrtm1 = new FieldElement(-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482); | |||
} | |||
} |
@@ -0,0 +1,155 @@ | |||
using System; | |||
namespace Discord.Net.ED25519 | |||
{ | |||
internal class Sha512 | |||
{ | |||
private Array8<ulong> _state; | |||
private readonly byte[] _buffer; | |||
private ulong _totalBytes; | |||
public const int BlockSize = 128; | |||
private static readonly byte[] _padding = new byte[] { 0x80 }; | |||
/// <summary> | |||
/// Allocation and initialization of the new SHA-512 object. | |||
/// </summary> | |||
public Sha512() | |||
{ | |||
_buffer = new byte[BlockSize];//todo: remove allocation | |||
Init(); | |||
} | |||
/// <summary> | |||
/// Performs an initialization of internal SHA-512 state. | |||
/// </summary> | |||
public void Init() | |||
{ | |||
Sha512Internal.Sha512Init(out _state); | |||
_totalBytes = 0; | |||
} | |||
/// <summary> | |||
/// Updates internal state with data from the provided array segment. | |||
/// </summary> | |||
/// <param name="data">Array segment</param> | |||
public void Update(ArraySegment<byte> data) | |||
{ | |||
Update(data.Array, data.Offset, data.Count); | |||
} | |||
/// <summary> | |||
/// Updates internal state with data from the provided array. | |||
/// </summary> | |||
/// <param name="data">Array of bytes</param> | |||
/// <param name="index">Offset of byte sequence</param> | |||
/// <param name="length">Sequence length</param> | |||
public void Update(byte[] data, int index, int length) | |||
{ | |||
Array16<ulong> block; | |||
int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); | |||
_totalBytes += (uint)length; | |||
if (_totalBytes >= ulong.MaxValue / 8) | |||
throw new InvalidOperationException("Too much data"); | |||
// Fill existing buffer | |||
if (bytesInBuffer != 0) | |||
{ | |||
var toCopy = Math.Min(BlockSize - bytesInBuffer, length); | |||
Buffer.BlockCopy(data, index, _buffer, bytesInBuffer, toCopy); | |||
index += toCopy; | |||
length -= toCopy; | |||
bytesInBuffer += toCopy; | |||
if (bytesInBuffer == BlockSize) | |||
{ | |||
ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); | |||
Sha512Internal.Core(out _state, ref _state, ref block); | |||
CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); | |||
bytesInBuffer = 0; | |||
} | |||
} | |||
// Hash complete blocks without copying | |||
while (length >= BlockSize) | |||
{ | |||
ByteIntegerConverter.Array16LoadBigEndian64(out block, data, index); | |||
Sha512Internal.Core(out _state, ref _state, ref block); | |||
index += BlockSize; | |||
length -= BlockSize; | |||
} | |||
// Copy remainder into buffer | |||
if (length > 0) | |||
{ | |||
Buffer.BlockCopy(data, index, _buffer, bytesInBuffer, length); | |||
} | |||
} | |||
/// <summary> | |||
/// Finalizes SHA-512 hashing | |||
/// </summary> | |||
/// <param name="output">Output buffer</param> | |||
public void Finalize(ArraySegment<byte> output) | |||
{ | |||
Preconditions.NotNull(output.Array, nameof(output)); | |||
if (output.Count != 64) | |||
throw new ArgumentException("Output should be 64 in length"); | |||
Update(_padding, 0, _padding.Length); | |||
Array16<ulong> block; | |||
ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); | |||
CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); | |||
int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); | |||
if (bytesInBuffer > BlockSize - 16) | |||
{ | |||
Sha512Internal.Core(out _state, ref _state, ref block); | |||
block = default(Array16<ulong>); | |||
} | |||
block.x15 = (_totalBytes - 1) * 8; | |||
Sha512Internal.Core(out _state, ref _state, ref block); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 0, _state.x0); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 8, _state.x1); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 16, _state.x2); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 24, _state.x3); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 32, _state.x4); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 40, _state.x5); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 48, _state.x6); | |||
ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 56, _state.x7); | |||
_state = default(Array8<ulong>); | |||
} | |||
/// <summary> | |||
/// Finalizes SHA-512 hashing. | |||
/// </summary> | |||
/// <returns>Hash bytes</returns> | |||
public byte[] Finalize() | |||
{ | |||
var result = new byte[64]; | |||
Finalize(new ArraySegment<byte>(result)); | |||
return result; | |||
} | |||
/// <summary> | |||
/// Calculates SHA-512 hash value for the given bytes array. | |||
/// </summary> | |||
/// <param name="data">Data bytes array</param> | |||
/// <returns>Hash bytes</returns> | |||
public static byte[] Hash(byte[] data) | |||
{ | |||
return Hash(data, 0, data.Length); | |||
} | |||
/// <summary> | |||
/// Calculates SHA-512 hash value for the given bytes array. | |||
/// </summary> | |||
/// <param name="data">Data bytes array</param> | |||
/// <param name="index">Offset of byte sequence</param> | |||
/// <param name="length">Sequence length</param> | |||
/// <returns>Hash bytes</returns> | |||
public static byte[] Hash(byte[] data, int index, int length) | |||
{ | |||
var hasher = new Sha512(); | |||
hasher.Update(data, index, length); | |||
return hasher.Finalize(); | |||
} | |||
} | |||
} |