From 580a7ec18e17d18d07aa87bcb39e7a2eb959e6ab Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Fri, 19 Oct 2018 10:37:51 -0400 Subject: [PATCH 01/10] Squashed 'CBOR/' content from commit d6d5e14 git-subtree-dir: CBOR git-subtree-split: d6d5e146f85695994acc9323077cb1fc7bddf7d3 --- CBOR.csproj | 73 + PeterO/BigInteger.cs | 127 + PeterO/BigIntegerExtra.cs | 43 + PeterO/Cbor/Base64.cs | 109 + PeterO/Cbor/CBORCanonical.cs | 88 + PeterO/Cbor/CBORDataUtilities.cs | 265 + PeterO/Cbor/CBORDateConverter.cs | 57 + PeterO/Cbor/CBORDouble.cs | 161 + PeterO/Cbor/CBOREInteger.cs | 133 + PeterO/Cbor/CBOREncodeOptions.cs | 109 + PeterO/Cbor/CBORException.cs | 29 + PeterO/Cbor/CBORExtendedDecimal.cs | 166 + PeterO/Cbor/CBORExtendedFloat.cs | 171 + PeterO/Cbor/CBORExtendedRational.cs | 166 + PeterO/Cbor/CBORInteger.cs | 136 + PeterO/Cbor/CBORJson.cs | 730 +++ PeterO/Cbor/CBORObject.cs | 4304 +++++++++++++++++ PeterO/Cbor/CBORObjectExtra.cs | 291 ++ PeterO/Cbor/CBORObjectFactory.cs | 8 + PeterO/Cbor/CBORObjectMath.cs | 382 ++ PeterO/Cbor/CBORReader.cs | 634 +++ PeterO/Cbor/CBORSingle.cs | 162 + PeterO/Cbor/CBORTag0.cs | 73 + PeterO/Cbor/CBORTag1.cs | 31 + PeterO/Cbor/CBORTag2.cs | 91 + PeterO/Cbor/CBORTag26.cs | 25 + PeterO/Cbor/CBORTag27.cs | 25 + PeterO/Cbor/CBORTag3.cs | 23 + PeterO/Cbor/CBORTag30.cs | 51 + PeterO/Cbor/CBORTag32.cs | 40 + PeterO/Cbor/CBORTag37.cs | 61 + PeterO/Cbor/CBORTag4.cs | 30 + PeterO/Cbor/CBORTag5.cs | 75 + PeterO/Cbor/CBORTagAny.cs | 27 + PeterO/Cbor/CBORTagGenericString.cs | 27 + PeterO/Cbor/CBORTagUnsigned.cs | 30 + PeterO/Cbor/CBORType.cs | 42 + PeterO/Cbor/CBORTypeFilter.cs | 399 ++ PeterO/Cbor/CBORTypeMapper.cs | 158 + PeterO/Cbor/CBORUriConverter.cs | 43 + PeterO/Cbor/CBORUtilities.cs | 719 +++ PeterO/Cbor/CBORUuidConverter.cs | 51 + PeterO/Cbor/CharacterInputWithCount.cs | 78 + PeterO/Cbor/CharacterReader.cs | 743 +++ PeterO/Cbor/FastInteger2.cs | 602 +++ PeterO/Cbor/ICBORConverter.cs | 19 + PeterO/Cbor/ICBORNumber.cs | 63 + PeterO/Cbor/ICBORTag.cs | 24 + PeterO/Cbor/ICBORToFromConverter.cs | 11 + PeterO/Cbor/ICharacterInput.cs | 15 + PeterO/Cbor/JSONOptions.cs | 28 + PeterO/Cbor/PODOptions.cs | 34 + PeterO/Cbor/PropertyMap.cs | 628 +++ PeterO/Cbor/PropertyMap2.cs | 808 ++++ PeterO/Cbor/SharedRefs.cs | 55 + PeterO/Cbor/StringOutput.cs | 114 + PeterO/Cbor/StringRefs.cs | 113 + PeterO/Cbor/URIUtility.cs | 1129 +++++ PeterO/DataUtilities.cs | 786 +++ PeterO/DebugUtility.cs | 41 + PeterO/ExtendedDecimal.cs | 229 + PeterO/ExtendedFloat.cs | 284 ++ PeterO/ExtendedRational.cs | 211 + PeterO/PrecisionContext.cs | 45 + PeterO/Rounding.cs | 60 + PeterO/TrapException.cs | 69 + Properties/AssemblyInfo.cs | 2 + docs.xml | 6113 ++++++++++++++++++++++++ 68 files changed, 22669 insertions(+) create mode 100644 CBOR.csproj create mode 100644 PeterO/BigInteger.cs create mode 100644 PeterO/BigIntegerExtra.cs create mode 100644 PeterO/Cbor/Base64.cs create mode 100644 PeterO/Cbor/CBORCanonical.cs create mode 100644 PeterO/Cbor/CBORDataUtilities.cs create mode 100644 PeterO/Cbor/CBORDateConverter.cs create mode 100644 PeterO/Cbor/CBORDouble.cs create mode 100644 PeterO/Cbor/CBOREInteger.cs create mode 100644 PeterO/Cbor/CBOREncodeOptions.cs create mode 100644 PeterO/Cbor/CBORException.cs create mode 100644 PeterO/Cbor/CBORExtendedDecimal.cs create mode 100644 PeterO/Cbor/CBORExtendedFloat.cs create mode 100644 PeterO/Cbor/CBORExtendedRational.cs create mode 100644 PeterO/Cbor/CBORInteger.cs create mode 100644 PeterO/Cbor/CBORJson.cs create mode 100644 PeterO/Cbor/CBORObject.cs create mode 100644 PeterO/Cbor/CBORObjectExtra.cs create mode 100644 PeterO/Cbor/CBORObjectFactory.cs create mode 100644 PeterO/Cbor/CBORObjectMath.cs create mode 100644 PeterO/Cbor/CBORReader.cs create mode 100644 PeterO/Cbor/CBORSingle.cs create mode 100644 PeterO/Cbor/CBORTag0.cs create mode 100644 PeterO/Cbor/CBORTag1.cs create mode 100644 PeterO/Cbor/CBORTag2.cs create mode 100644 PeterO/Cbor/CBORTag26.cs create mode 100644 PeterO/Cbor/CBORTag27.cs create mode 100644 PeterO/Cbor/CBORTag3.cs create mode 100644 PeterO/Cbor/CBORTag30.cs create mode 100644 PeterO/Cbor/CBORTag32.cs create mode 100644 PeterO/Cbor/CBORTag37.cs create mode 100644 PeterO/Cbor/CBORTag4.cs create mode 100644 PeterO/Cbor/CBORTag5.cs create mode 100644 PeterO/Cbor/CBORTagAny.cs create mode 100644 PeterO/Cbor/CBORTagGenericString.cs create mode 100644 PeterO/Cbor/CBORTagUnsigned.cs create mode 100644 PeterO/Cbor/CBORType.cs create mode 100644 PeterO/Cbor/CBORTypeFilter.cs create mode 100644 PeterO/Cbor/CBORTypeMapper.cs create mode 100644 PeterO/Cbor/CBORUriConverter.cs create mode 100644 PeterO/Cbor/CBORUtilities.cs create mode 100644 PeterO/Cbor/CBORUuidConverter.cs create mode 100644 PeterO/Cbor/CharacterInputWithCount.cs create mode 100644 PeterO/Cbor/CharacterReader.cs create mode 100644 PeterO/Cbor/FastInteger2.cs create mode 100644 PeterO/Cbor/ICBORConverter.cs create mode 100644 PeterO/Cbor/ICBORNumber.cs create mode 100644 PeterO/Cbor/ICBORTag.cs create mode 100644 PeterO/Cbor/ICBORToFromConverter.cs create mode 100644 PeterO/Cbor/ICharacterInput.cs create mode 100644 PeterO/Cbor/JSONOptions.cs create mode 100644 PeterO/Cbor/PODOptions.cs create mode 100644 PeterO/Cbor/PropertyMap.cs create mode 100644 PeterO/Cbor/PropertyMap2.cs create mode 100644 PeterO/Cbor/SharedRefs.cs create mode 100644 PeterO/Cbor/StringOutput.cs create mode 100644 PeterO/Cbor/StringRefs.cs create mode 100644 PeterO/Cbor/URIUtility.cs create mode 100644 PeterO/DataUtilities.cs create mode 100644 PeterO/DebugUtility.cs create mode 100644 PeterO/ExtendedDecimal.cs create mode 100644 PeterO/ExtendedFloat.cs create mode 100644 PeterO/ExtendedRational.cs create mode 100644 PeterO/PrecisionContext.cs create mode 100644 PeterO/Rounding.cs create mode 100644 PeterO/TrapException.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 docs.xml diff --git a/CBOR.csproj b/CBOR.csproj new file mode 100644 index 00000000..63e2742e --- /dev/null +++ b/CBOR.csproj @@ -0,0 +1,73 @@ + + + + netstandard1.0 + True + 3.4.0-beta1 + Peter Occil + A C# implementation of Concise Binary Object Representation (CBOR), a general-purpose binary data format defined in RFC 7049. + A C# implementation of Concise Binary Object Representation (CBOR), a general-purpose binary data format defined in RFC 7049. + Written by Peter O. in 2013-2018. Any copyright is released to the Public Domain. + Peter Occil + PeterO.Cbor + http://creativecommons.org/publicdomain/zero/1.0/ + https://github.com/peteroupc/CBOR + +Version 3.4.0-beta1: + +- Improved implemenation of new ToObject method. +- Bugs in multidimensional array serialization by FromObject were fixed. +- URI parsing restored to 3.0 version for backward compatibility. +- Remove method disallows null for backward compatibility. +- ICBORObjectConverter renamed to ICBORToFromConverter. +- Several APIs were obsoleted. + +Version 3.4.0-alpha1: + +- Add ToObject method for deserializing CBOR objects. +- Add ICBORObjectConverter interface. +- Add HasMostOuterTag method to CBORObject class. +- Add CTAP2 canonicalization support to CBOR object encoding. +- Added examples in several places in documentation. + + + cbor data serialization binary json + True + PeterO.snk + CBOR (Concise Binary Object Representation) + true + + + + bin\Debug\netstandard1.0\CBOR.xml + + + + Custom + Signing Workaround + sn -R bin/Debug/netstandard1.0/CBOR.dll PeterO.snk + ${ProjectDir} + True + + + + + + + bin\Release\netstandard1.0\CBOR.xml + + + + Custom + Signing Workaround + sn -R bin/Release/netstandard1.0/CBOR.dll PeterO.snk + ${ProjectDir} + True + + + + + + + + \ No newline at end of file diff --git a/PeterO/BigInteger.cs b/PeterO/BigInteger.cs new file mode 100644 index 00000000..15a67dfe --- /dev/null +++ b/PeterO/BigInteger.cs @@ -0,0 +1,127 @@ +/* +Written by Peter O. in 2013. + +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO { + /// +[Obsolete( + "Use EInteger from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public sealed partial class BigInteger : IComparable, + IEquatable { + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "BigInteger is immutable")] +#endif + + [Obsolete("Use EInteger from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly BigInteger ONE = new BigInteger(EInteger.One); + + private static readonly BigInteger ValueOneValue = new + BigInteger(EInteger.One); + + private readonly EInteger ei; + + internal BigInteger(EInteger ei) { + if (ei == null) { + throw new ArgumentNullException(nameof(ei)); +} + this.ei = ei; + } + + internal static BigInteger ToLegacy(EInteger ei) { + return new BigInteger(ei); + } + + internal static EInteger FromLegacy(BigInteger bei) { + return bei.Ei; + } + + private static readonly BigInteger ValueZeroValue = new + BigInteger(EInteger.Zero); + + internal EInteger Ei { + get { + return this.ei; + } + } + + /// + public static BigInteger fromBytes(byte[] bytes, bool littleEndian) { + return new BigInteger(EInteger.FromBytes(bytes, littleEndian)); + } + + /// + public static BigInteger fromRadixString(string str, int radix) { + return new BigInteger(EInteger.FromRadixString(str, radix)); + } + + /// + public static BigInteger fromString(string str) { +return new BigInteger(EInteger.FromString(str)); +} + + /// + public static BigInteger valueOf(long longerValue) { + return new BigInteger(EInteger.FromInt64(longerValue)); + } + + /// + public int bitLength() { +return this.Ei.GetSignedBitLength(); + } + + /// + public override bool Equals(object obj) { + var bi = obj as BigInteger; + return (bi == null) ? false : this.Ei.Equals(bi.Ei); +} + + /// + public override int GetHashCode() { + return this.Ei.GetHashCode(); + } + + /// + public byte[] toBytes(bool littleEndian) { + return this.Ei.ToBytes(littleEndian); + } + + /// + public string toRadixString(int radix) { + return this.Ei.ToRadixString(radix); + } + + /// + public override string ToString() { + return this.Ei.ToString(); + } + + /// + public int CompareTo(BigInteger other) { + return this.Ei.CompareTo(other == null ? null : other.Ei); + } + } +} diff --git a/PeterO/BigIntegerExtra.cs b/PeterO/BigIntegerExtra.cs new file mode 100644 index 00000000..c28d42e7 --- /dev/null +++ b/PeterO/BigIntegerExtra.cs @@ -0,0 +1,43 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO { + /// + public sealed partial class BigInteger { + /// + [CLSCompliant(false)] [Obsolete( + "Use EInteger from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public static BigInteger Zero { + get { + return ValueZeroValue; + } + } + + /// + [CLSCompliant(false)] [Obsolete( + "Use EInteger from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public static BigInteger One { + get { + return ValueOneValue; + } + } + + /// + [Obsolete( + "Use EInteger from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public bool Equals(BigInteger other) { + return this.Equals((object)other); + } + } +} diff --git a/PeterO/Cbor/Base64.cs b/PeterO/Cbor/Base64.cs new file mode 100644 index 00000000..3fe209e1 --- /dev/null +++ b/PeterO/Cbor/Base64.cs @@ -0,0 +1,109 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Text; + +namespace PeterO.Cbor { + internal static class Base64 { + private const string Base64URL = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + private const string Base64Classic = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + public static void WriteBase64( + StringOutput writer, + byte[] data, + int offset, + int count, + bool padding) { + WriteBase64(writer, data, offset, count, true, padding); + } + + public static void WriteBase64URL( + StringOutput writer, + byte[] data, + int offset, + int count, + bool padding) { + WriteBase64(writer, data, offset, count, false, padding); + } + + private static void WriteBase64( + StringOutput writer, + byte[] data, + int offset, + int count, + bool classic, + bool padding) { + if (writer == null) { + throw new ArgumentNullException(nameof(writer)); + } + if (offset < 0) { + throw new ArgumentException("offset (" + offset + ") is less than " + + "0 "); + } + if (offset > data.Length) { + throw new ArgumentException("offset (" + offset + ") is more than " + + data.Length); + } + if (count < 0) { + throw new ArgumentException("count (" + count + ") is less than " + + "0 "); + } + if (count > data.Length) { + throw new ArgumentException("count (" + count + ") is more than " + + data.Length); + } + if (data.Length - offset < count) { + throw new ArgumentException("data's length minus " + offset + " (" + + (data.Length - offset) + ") is less than " + count); + } + string alphabet = classic ? Base64Classic : Base64URL; + int length = offset + count; + int i = offset; + var buffer = new char[4]; + for (i = offset; i < (length - 2); i += 3) { + buffer[0] = (char)alphabet[(data[i] >> 2) & 63]; + buffer[1] = (char)alphabet[((data[i] & 3) << 4) + + ((data[i + 1] >> 4) & 15)]; + buffer[2] = (char)alphabet[((data[i + 1] & 15) << 2) + ((data[i + + 2] >> 6) & 3)]; + buffer[3] = (char)alphabet[data[i + 2] & 63]; + writer.WriteCodePoint((int)buffer[0]); + writer.WriteCodePoint((int)buffer[1]); + writer.WriteCodePoint((int)buffer[2]); + writer.WriteCodePoint((int)buffer[3]); + } + int lenmod3 = count % 3; + if (lenmod3 != 0) { + i = length - lenmod3; + buffer[0] = (char)alphabet[(data[i] >> 2) & 63]; + if (lenmod3 == 2) { + buffer[1] = (char)alphabet[((data[i] & 3) << 4) + ((data[i + 1] >> + 4) & 15)]; + buffer[2] = (char)alphabet[(data[i + 1] & 15) << 2]; + writer.WriteCodePoint((int)buffer[0]); + writer.WriteCodePoint((int)buffer[1]); + writer.WriteCodePoint((int)buffer[2]); + if (padding) { + writer.WriteCodePoint((int)'='); + } + } else { + buffer[1] = (char)alphabet[(data[i] & 3) << 4]; + writer.WriteCodePoint((int)buffer[0]); + writer.WriteCodePoint((int)buffer[1]); + if (padding) { + writer.WriteCodePoint((int)'='); + writer.WriteCodePoint((int)'='); + } + } + } + } + } +} diff --git a/PeterO/Cbor/CBORCanonical.cs b/PeterO/Cbor/CBORCanonical.cs new file mode 100644 index 00000000..8cc18c11 --- /dev/null +++ b/PeterO/Cbor/CBORCanonical.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace PeterO.Cbor { + internal static class CBORCanonical { + private sealed class CtapComparer : IComparer { + public int Compare(CBORObject a, CBORObject b) { + byte[] abs; + byte[] bbs; +bool bothBytes = false; + if (a.Type == CBORType.ByteString && b.Type == CBORType.ByteString) { + abs = a.GetByteString(); + bbs = b.GetByteString(); +bothBytes = true; + } else { + abs = CtapCanonicalEncode(a); + bbs = CtapCanonicalEncode(b); + } +if (!bothBytes && (abs[0] & 0xe0) != (bbs[0] & 0xe0)) { + // different major types + return (abs[0] & 0xe0) < (bbs[0] & 0xe0) ? -1 : 1; +} + if (abs.Length != bbs.Length) { + // different lengths + return abs.Length < bbs.Length ? -1 : 1; +} + for (var i = 0; i < abs.Length; ++i) { + if (abs[i] != bbs[i]) { + int ai = ((int)abs[i]) & 0xff; + int bi = ((int)bbs[i]) & 0xff; + return (ai < bi) ? -1 : 1; + } + } + return 0; + } + } + + public static byte[] CtapCanonicalEncode(CBORObject a) { + CBORObject cbor = a.Untag(); + CBORType valueAType = cbor.Type; + try { + if (valueAType == CBORType.Array) { + using (var ms = new MemoryStream()) { + CBORObject.WriteValue(ms, 4, cbor.Count); + for (var i = 0; i < cbor.Count; ++i) { + byte[] bytes = CtapCanonicalEncode(cbor[i]); + ms.Write(bytes, 0, bytes.Length); + } + return ms.ToArray(); + } + } else if (valueAType == CBORType.Map) { + var sortedKeys = new List(); + foreach (CBORObject key in cbor.Keys) { + sortedKeys.Add(key); + } + sortedKeys.Sort(new CtapComparer()); + using (var ms = new MemoryStream()) { + CBORObject.WriteValue(ms, 5, cbor.Count); + foreach (CBORObject key in sortedKeys) { + byte[] bytes = CtapCanonicalEncode(key); + ms.Write(bytes, 0, bytes.Length); + bytes = CtapCanonicalEncode(cbor[key]); + ms.Write(bytes, 0, bytes.Length); + } + return ms.ToArray(); + } + } + } catch (IOException ex) { + throw new InvalidOperationException(ex.ToString(), ex); + } + if (valueAType == CBORType.SimpleValue || + valueAType == CBORType.Boolean || valueAType == CBORType.ByteString || + valueAType == CBORType.TextString) { + return cbor.EncodeToBytes(CBOREncodeOptions.Default); + } else if (valueAType == CBORType.Number) { + if (cbor.CanFitInInt64()) { + return cbor.EncodeToBytes(CBOREncodeOptions.Default); + } else { + cbor = CBORObject.FromObject(cbor.AsDouble()); + return cbor.EncodeToBytes(CBOREncodeOptions.Default); + } + } else { + throw new ArgumentException("Invalid CBOR type."); + } + } + } +} diff --git a/PeterO/Cbor/CBORDataUtilities.cs b/PeterO/Cbor/CBORDataUtilities.cs new file mode 100644 index 00000000..bb86858d --- /dev/null +++ b/PeterO/Cbor/CBORDataUtilities.cs @@ -0,0 +1,265 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + /// + public static class CBORDataUtilities { + private const int MaxSafeInt = 214748363; + + /// + public static CBORObject ParseJSONNumber(string str) { + return ParseJSONNumber(str, false, false); + } + + /// + public static CBORObject ParseJSONNumber( + string str, + bool integersOnly, + bool positiveOnly) { + return ParseJSONNumber(str, integersOnly, positiveOnly, false); + } + + /// + public static CBORObject ParseJSONNumber( + string str, + bool integersOnly, + bool positiveOnly, + bool preserveNegativeZero) { + if (String.IsNullOrEmpty(str)) { + return null; + } + var offset = 0; + var negative = false; + if (str[0] == '-' && !positiveOnly) { + negative = true; + ++offset; + } + var mantInt = 0; + FastInteger2 mant = null; + var mantBuffer = 0; + var mantBufferMult = 1; + var expBuffer = 0; + var expBufferMult = 1; + var haveDecimalPoint = false; + var haveDigits = false; + var haveDigitsAfterDecimal = false; + var haveExponent = false; + var newScaleInt = 0; + FastInteger2 newScale = null; + int i = offset; + // Ordinary number + if (i < str.Length && str[i] == '0') { + ++i; + haveDigits = true; + if (i == str.Length) { + if (preserveNegativeZero && negative) { + return CBORObject.FromObject( + EDecimal.NegativeZero); + } + return CBORObject.FromObject(0); + } + if (!integersOnly) { + if (str[i] == '.') { + haveDecimalPoint = true; + ++i; + } else if (str[i] == 'E' || str[i] == 'e') { + haveExponent = true; + } else { + return null; + } + } else { + return null; + } + } + for (; i < str.Length; ++i) { + if (str[i] >= '0' && str[i] <= '9') { + var thisdigit = (int)(str[i] - '0'); + if (mantInt > MaxSafeInt) { + if (mant == null) { + mant = new FastInteger2(mantInt); + mantBuffer = thisdigit; + mantBufferMult = 10; + } else { + if (mantBufferMult >= 1000000000) { + mant.Multiply(mantBufferMult).AddInt(mantBuffer); + mantBuffer = thisdigit; + mantBufferMult = 10; + } else { + mantBufferMult *= 10; + mantBuffer = (mantBuffer << 3) + (mantBuffer << 1); + mantBuffer += thisdigit; + } + } + } else { + mantInt *= 10; + mantInt += thisdigit; + } + haveDigits = true; + if (haveDecimalPoint) { + haveDigitsAfterDecimal = true; + if (newScaleInt == Int32.MinValue) { +newScale = newScale ?? (new FastInteger2(newScaleInt)); + newScale.AddInt(-1); + } else { + --newScaleInt; + } + } + } else if (!integersOnly && str[i] == '.') { + if (!haveDigits) { + // no digits before the decimal point + return null; + } + if (haveDecimalPoint) { + return null; + } + haveDecimalPoint = true; + } else if (!integersOnly && (str[i] == 'E' || str[i] == 'e')) { + haveExponent = true; + ++i; + break; + } else { + return null; + } + } + if (!haveDigits || (haveDecimalPoint && !haveDigitsAfterDecimal)) { + return null; + } + if (mant != null && (mantBufferMult != 1 || mantBuffer != 0)) { + mant.Multiply(mantBufferMult).AddInt(mantBuffer); + } + if (haveExponent) { + FastInteger2 exp = null; + var expInt = 0; + offset = 1; + haveDigits = false; + if (i == str.Length) { + return null; + } + if (str[i] == '+' || str[i] == '-') { + if (str[i] == '-') { + offset = -1; + } + ++i; + } + for (; i < str.Length; ++i) { + if (str[i] >= '0' && str[i] <= '9') { + haveDigits = true; + var thisdigit = (int)(str[i] - '0'); + if (expInt > MaxSafeInt) { + if (exp == null) { + exp = new FastInteger2(expInt); + expBuffer = thisdigit; + expBufferMult = 10; + } else { + if (expBufferMult >= 1000000000) { + exp.Multiply(expBufferMult).AddInt(expBuffer); + expBuffer = thisdigit; + expBufferMult = 10; + } else { + // multiply expBufferMult and expBuffer each by 10 + expBufferMult = (expBufferMult << 3) + (expBufferMult << 1); + expBuffer = (expBuffer << 3) + (expBuffer << 1); + expBuffer += thisdigit; + } + } + } else { + expInt *= 10; + expInt += thisdigit; + } + } else { + return null; + } + } + if (!haveDigits) { + return null; + } + if (exp != null && (expBufferMult != 1 || expBuffer != 0)) { + exp.Multiply(expBufferMult).AddInt(expBuffer); + } + if (offset >= 0 && newScaleInt == 0 && newScale == null && exp == null) { + newScaleInt = expInt; + } else if (exp == null) { +newScale = newScale ?? (new FastInteger2(newScaleInt)); + if (offset < 0) { + newScale.SubtractInt(expInt); + } else if (expInt != 0) { + newScale.AddInt(expInt); + } + } else { +newScale = newScale ?? (new FastInteger2(newScaleInt)); + if (offset < 0) { + newScale.Subtract(exp); + } else { + newScale.Add(exp); + } + } + } + if (i != str.Length) { + // End of the string wasn't reached, so isn't a number + return null; + } + if ((newScale == null && newScaleInt == 0) || (newScale != null && + newScale.Sign == 0)) { + // No fractional part + if (mant != null && mant.CanFitInInt32()) { + mantInt = mant.AsInt32(); + mant = null; + } + if (mant == null) { + // NOTE: mantInt can only be 0 or greater, so overflow is impossible +#if DEBUG + if (mantInt < 0) { + throw new ArgumentException("mantInt (" + mantInt + + ") is less than 0"); + } +#endif + + if (negative) { + mantInt = -mantInt; + if (preserveNegativeZero && mantInt == 0) { + return CBORObject.FromObject( + EDecimal.NegativeZero); + } + } + return CBORObject.FromObject(mantInt); + } else { + EInteger bigmant2 = mant.AsBigInteger(); + if (negative) { + bigmant2 = -(EInteger)bigmant2; + } + return CBORObject.FromObject(bigmant2); + } + } else { + EInteger bigmant = (mant == null) ? ((EInteger)mantInt) : + mant.AsBigInteger(); + EInteger bigexp = (newScale == null) ? ((EInteger)newScaleInt) : + newScale.AsBigInteger(); + if (negative) { + bigmant = -(EInteger)bigmant; + } + EDecimal edec; + edec = EDecimal.Create( + bigmant, + bigexp); + if (negative && preserveNegativeZero && bigmant.IsZero) { + EDecimal negzero = EDecimal.NegativeZero; + negzero = negzero.Quantize(bigexp, null); + edec = negzero.Subtract(edec); + } + return CBORObject.FromObject(edec); + } + } + } +} diff --git a/PeterO/Cbor/CBORDateConverter.cs b/PeterO/Cbor/CBORDateConverter.cs new file mode 100644 index 00000000..07740bcc --- /dev/null +++ b/PeterO/Cbor/CBORDateConverter.cs @@ -0,0 +1,57 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBORDateConverter : ICBORToFromConverter { + private static string DateTimeToString(DateTime bi) { + var lesserFields = new int[7]; + var year = new EInteger[1]; + PropertyMap.BreakDownDateTime(bi, year, lesserFields); + return CBORUtilities.ToAtomDateTimeString(year[0], lesserFields, true); + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.TextString) { + throw new CBORException("Not a text string"); + } + return obj; + } + + public DateTime FromCBORObject(CBORObject obj) { + if (obj.HasMostOuterTag(0)) { + return StringToDateTime(obj.AsString()); + } else if (obj.HasMostOuterTag(1)) { + if (!obj.IsFinite) { + throw new CBORException("Not a finite number"); + } + EDecimal dec = obj.AsEDecimal(); + var lesserFields = new int[7]; + var year = new EInteger[1]; + CBORUtilities.BreakDownSecondsSinceEpoch( + dec, + year, + lesserFields); + return PropertyMap.BuildUpDateTime(year[0], lesserFields); + } + throw new CBORException("Not tag 0 or 1"); + } + + public static DateTime StringToDateTime(string str) { + var lesserFields = new int[7]; + var year = new EInteger[1]; + CBORUtilities.ParseAtomDateTimeString(str, year, lesserFields); + return PropertyMap.BuildUpDateTime(year[0], lesserFields); + } + + public CBORObject ToCBORObject(DateTime obj) { + return CBORObject.FromObjectAndTag(DateTimeToString(obj), 0); + } + } +} diff --git a/PeterO/Cbor/CBORDouble.cs b/PeterO/Cbor/CBORDouble.cs new file mode 100644 index 00000000..a206fbc3 --- /dev/null +++ b/PeterO/Cbor/CBORDouble.cs @@ -0,0 +1,161 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBORDouble : ICBORNumber + { + public bool IsPositiveInfinity(object obj) { + return Double.IsPositiveInfinity((double)obj); + } + + public bool IsInfinity(object obj) { + return Double.IsInfinity((double)obj); + } + + public bool IsNegativeInfinity(object obj) { + return Double.IsNegativeInfinity((double)obj); + } + + public bool IsNaN(object obj) { + return Double.IsNaN((double)obj); + } + + public double AsDouble(object obj) { + return (double)obj; + } + + public EDecimal AsExtendedDecimal(object obj) { + return EDecimal.FromDouble((double)obj); + } + + public EFloat AsExtendedFloat(object obj) { + return EFloat.FromDouble((double)obj); + } + + public float AsSingle(object obj) { + return (float)(double)obj; + } + + public EInteger AsEInteger(object obj) { + return CBORUtilities.BigIntegerFromDouble((double)obj); + } + + public long AsInt64(object obj) { + var fltItem = (double)obj; + if (Double.IsNaN(fltItem)) { + throw new OverflowException("This object's value is out of range"); + } + fltItem = (fltItem < 0) ? Math.Ceiling(fltItem) : Math.Floor(fltItem); + if (fltItem >= -9223372036854775808.0 && fltItem < + 9223372036854775808.0) { + return (long)fltItem; + } + throw new OverflowException("This object's value is out of range"); + } + + public bool CanFitInSingle(object obj) { + var fltItem = (double)obj; + if (Double.IsNaN(fltItem)) { + return true; + } + var sing = (float)fltItem; + return (double)sing == (double)fltItem; + } + + public bool CanFitInDouble(object obj) { + return true; + } + + public bool CanFitInInt32(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt32(obj); + } + + public bool CanFitInInt64(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt64(obj); + } + + public bool CanTruncatedIntFitInInt64(object obj) { + var fltItem = (double)obj; + if (Double.IsNaN(fltItem) || Double.IsInfinity(fltItem)) { + return false; + } + double fltItem2 = (fltItem < 0) ? Math.Ceiling(fltItem) : + Math.Floor(fltItem); + return fltItem2 >= -9223372036854775808.0 && fltItem2 < + 9223372036854775808.0; + } + + public bool CanTruncatedIntFitInInt32(object obj) { + var fltItem = (double)obj; + if (Double.IsNaN(fltItem) || Double.IsInfinity(fltItem)) { + return false; + } + double fltItem2 = (fltItem < 0) ? Math.Ceiling(fltItem) : + Math.Floor(fltItem); + return fltItem2 >= Int32.MinValue && fltItem2 <= Int32.MaxValue; + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var fltItem = (double)obj; + if (Double.IsNaN(fltItem)) { + throw new OverflowException("This object's value is out of range"); + } + fltItem = (fltItem < 0) ? Math.Ceiling(fltItem) : Math.Floor(fltItem); + if (fltItem >= minValue && fltItem <= maxValue) { + var ret = (int)fltItem; + return ret; + } + throw new OverflowException("This object's value is out of range"); + } + + public bool IsZero(object obj) { + return (double)obj == 0.0; + } + + public int Sign(object obj) { + var flt = (double)obj; + return Double.IsNaN(flt) ? 2 : ((double)flt == 0.0 ? 0 : (flt < 0.0f ? + -1 : 1)); + } + + public bool IsIntegral(object obj) { + var fltItem = (double)obj; + if (Double.IsNaN(fltItem) || Double.IsInfinity(fltItem)) { + return false; + } + double fltItem2 = (fltItem < 0) ? Math.Ceiling(fltItem) : + Math.Floor(fltItem); + return fltItem == fltItem2; + } + + public object Negate(object obj) { + var val = (double)obj; + return -val; + } + + public object Abs(object obj) { + var val = (double)obj; + return (val < 0) ? -val : obj; + } + + public ERational AsExtendedRational(object obj) { + return ERational.FromDouble((double)obj); + } + + public bool IsNegative(object obj) { + var dbl = (double)obj; + long lvalue = BitConverter.ToInt64( + BitConverter.GetBytes((double)dbl), + 0); + return (lvalue >> 63) != 0; + } + } +} diff --git a/PeterO/Cbor/CBOREInteger.cs b/PeterO/Cbor/CBOREInteger.cs new file mode 100644 index 00000000..66b61be5 --- /dev/null +++ b/PeterO/Cbor/CBOREInteger.cs @@ -0,0 +1,133 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBOREInteger : ICBORNumber + { + public bool IsPositiveInfinity(object obj) { + return false; + } + + public bool IsInfinity(object obj) { + return false; + } + + public bool IsNegativeInfinity(object obj) { + return false; + } + + public bool IsNaN(object obj) { + return false; + } + + public double AsDouble(object obj) { + return EFloat.FromEInteger((EInteger)obj).ToDouble(); + } + + public EDecimal AsExtendedDecimal(object obj) { + return EDecimal.FromEInteger((EInteger)obj); + } + + public EFloat AsExtendedFloat(object obj) { + return EFloat.FromEInteger((EInteger)obj); + } + + public float AsSingle(object obj) { + return EFloat.FromEInteger((EInteger)obj).ToSingle(); + } + + public EInteger AsEInteger(object obj) { + return (EInteger)obj; + } + + public long AsInt64(object obj) { + var bi = (EInteger)obj; + if (bi.CompareTo(CBORObject.Int64MaxValue) > 0 || + bi.CompareTo(CBORObject.Int64MinValue) < 0) { + throw new OverflowException("This object's value is out of range"); + } + return (long)bi; + } + + public bool CanFitInSingle(object obj) { + var bigintItem = (EInteger)obj; + EFloat ef = EFloat.FromEInteger(bigintItem); + EFloat ef2 = EFloat.FromSingle(ef.ToSingle()); + return ef.CompareTo(ef2) == 0; + } + + public bool CanFitInDouble(object obj) { + var bigintItem = (EInteger)obj; + EFloat ef = EFloat.FromEInteger(bigintItem); + EFloat ef2 = EFloat.FromDouble(ef.ToDouble()); + return ef.CompareTo(ef2) == 0; + } + + public bool CanFitInInt32(object obj) { + var bi = (EInteger)obj; + return bi.CanFitInInt32(); + } + + public bool CanFitInInt64(object obj) { + var bi = (EInteger)obj; + return bi.GetSignedBitLength() <= 63; + } + + public bool CanTruncatedIntFitInInt64(object obj) { + return this.CanFitInInt64(obj); + } + + public bool CanTruncatedIntFitInInt32(object obj) { + return this.CanFitInInt32(obj); + } + + public bool IsZero(object obj) { + return ((EInteger)obj).IsZero; + } + + public int Sign(object obj) { + return ((EInteger)obj).Sign; + } + + public bool IsIntegral(object obj) { + return true; + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var bi = (EInteger)obj; + if (bi.CanFitInInt32()) { + var ret = (int)bi; + if (ret >= minValue && ret <= maxValue) { + return ret; + } + } + throw new OverflowException("This object's value is out of range"); + } + + public object Negate(object obj) { + var bigobj = (EInteger)obj; + bigobj = -(EInteger)bigobj; + return bigobj; + } + + public object Abs(object obj) { + return ((EInteger)obj).Abs(); + } + + public ERational AsExtendedRational(object obj) { + return ERational.FromEInteger((EInteger)obj); + } + + public bool IsNegative(object obj) { + return ((EInteger)obj).Sign < 0; + } + } +} diff --git a/PeterO/Cbor/CBOREncodeOptions.cs b/PeterO/Cbor/CBOREncodeOptions.cs new file mode 100644 index 00000000..c8024b85 --- /dev/null +++ b/PeterO/Cbor/CBOREncodeOptions.cs @@ -0,0 +1,109 @@ +using System; + +namespace PeterO.Cbor { + /// + public sealed class CBOREncodeOptions { + /// + [Obsolete("Use 'new CBOREncodeOptions(true,true)' instead. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated. 'CBOREncodeOptions.Default' contains recommended default options that may be adopted by certain CBORObject methods in the next major version.")] + public static readonly CBOREncodeOptions None = + new CBOREncodeOptions(0); + + /// + public static readonly CBOREncodeOptions Default = + new CBOREncodeOptions(false, false); + + /// + [Obsolete("Use 'new CBOREncodeOptions(false,true)' instead. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] + public static readonly CBOREncodeOptions NoIndefLengthStrings = + new CBOREncodeOptions(1); + + /// + [Obsolete("Use 'new CBOREncodeOptions(true,false)' instead. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] + public static readonly CBOREncodeOptions NoDuplicateKeys = + new CBOREncodeOptions(2); + + private readonly int value; + + /// + public CBOREncodeOptions() : this(false, false) { +} + + /// + public CBOREncodeOptions( + bool useIndefLengthStrings, + bool allowDuplicateKeys) : + this(useIndefLengthStrings, allowDuplicateKeys, false) { + } + + /// + public CBOREncodeOptions( + bool useIndefLengthStrings, + bool allowDuplicateKeys, + bool ctap2Canonical) { + var val = 0; + if (!useIndefLengthStrings) { + val |= 1; + } + if (!allowDuplicateKeys) { + val |= 2; + } + this.value = val; + this.Ctap2Canonical = ctap2Canonical; + } + + /// + public bool UseIndefLengthStrings { + get { + return (this.value & 1) == 0; + } + } + + /// + public bool AllowDuplicateKeys { + get { + return (this.value & 2) == 0; + } + } + + /// + public bool Ctap2Canonical { get; private set; } + + /// + [Obsolete("Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] + public int Value { + get { + return this.value; + } + } + + private CBOREncodeOptions(int value) : + this((value & 1) == 0, (value & 2) == 0) { + } + + /// + [Obsolete("May be removed in a later version. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] + public CBOREncodeOptions Or(CBOREncodeOptions o) { + return new CBOREncodeOptions(this.value | o.value); + } + + /// + [Obsolete("May be removed in a later version. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] + public CBOREncodeOptions And(CBOREncodeOptions o) { + return new CBOREncodeOptions(this.value & o.value); + } + } +} diff --git a/PeterO/Cbor/CBORException.cs b/PeterO/Cbor/CBORException.cs new file mode 100644 index 00000000..c1632bb0 --- /dev/null +++ b/PeterO/Cbor/CBORException.cs @@ -0,0 +1,29 @@ +/* +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + /// + public class CBORException : Exception { + /// + public CBORException() { + } + + /// + public CBORException(string message) : base(message) { + } + + /// + public CBORException(string message, Exception innerException) : + base(message, innerException) { + } + } +} diff --git a/PeterO/Cbor/CBORExtendedDecimal.cs b/PeterO/Cbor/CBORExtendedDecimal.cs new file mode 100644 index 00000000..69407265 --- /dev/null +++ b/PeterO/Cbor/CBORExtendedDecimal.cs @@ -0,0 +1,166 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBORExtendedDecimal : ICBORNumber + { + public bool IsPositiveInfinity(object obj) { + var ed = (EDecimal)obj; + return ed.IsPositiveInfinity(); + } + + public bool IsInfinity(object obj) { + var ed = (EDecimal)obj; + return ed.IsInfinity(); + } + + public bool IsNegativeInfinity(object obj) { + var ed = (EDecimal)obj; + return ed.IsNegativeInfinity(); + } + + public bool IsNaN(object obj) { + var ed = (EDecimal)obj; + return ed.IsNaN(); + } + + public double AsDouble(object obj) { + var ed = (EDecimal)obj; + return ed.ToDouble(); + } + + public EDecimal AsExtendedDecimal(object obj) { + var ed = (EDecimal)obj; + return ed; + } + + public EFloat AsExtendedFloat(object obj) { + var ed = (EDecimal)obj; + return ed.ToEFloat(); + } + + public float AsSingle(object obj) { + var ed = (EDecimal)obj; + return ed.ToSingle(); + } + + public EInteger AsEInteger(object obj) { + var ed = (EDecimal)obj; + return ed.ToEInteger(); + } + + public long AsInt64(object obj) { + var ef = (EDecimal)obj; + if (this.CanTruncatedIntFitInInt64(obj)) { + EInteger bi = ef.ToEInteger(); + return (long)bi; + } + throw new OverflowException("This object's value is out of range"); + } + + public bool CanFitInSingle(object obj) { + var ef = (EDecimal)obj; + return (!ef.IsFinite) || + (ef.CompareTo(EDecimal.FromSingle(ef.ToSingle())) == 0); + } + + public bool CanFitInDouble(object obj) { + var ef = (EDecimal)obj; + return (!ef.IsFinite) || + (ef.CompareTo(EDecimal.FromDouble(ef.ToDouble())) == 0); + } + + public bool CanFitInInt32(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt32(obj); + } + + public bool CanFitInInt64(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt64(obj); + } + + public bool CanTruncatedIntFitInInt64(object obj) { + var ef = (EDecimal)obj; + if (!ef.IsFinite) { + return false; + } + if (ef.IsZero) { + return true; + } + if (ef.Exponent.CompareTo((EInteger)21) >= 0) { + return false; + } + EInteger bi = ef.ToEInteger(); + return bi.GetSignedBitLength() <= 63; + } + + public bool CanTruncatedIntFitInInt32(object obj) { + var ef = (EDecimal)obj; + if (!ef.IsFinite) { + return false; + } + if (ef.IsZero) { + return true; + } + if (ef.Exponent.CompareTo((EInteger)11) >= 0) { + return false; + } + EInteger bi = ef.ToEInteger(); + return bi.CanFitInInt32(); + } + + public bool IsZero(object obj) { + var ed = (EDecimal)obj; + return ed.IsZero; + } + + public int Sign(object obj) { + var ed = (EDecimal)obj; + return ed.IsNaN() ? 2 : ed.Sign; + } + + public bool IsIntegral(object obj) { + var ed = (EDecimal)obj; + return ed.IsFinite && ((ed.Exponent.Sign >= 0) || + (ed.CompareTo(EDecimal.FromEInteger(ed.ToEInteger())) == + 0)); + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var ef = (EDecimal)obj; + if (this.CanTruncatedIntFitInInt32(obj)) { + EInteger bi = ef.ToEInteger(); + var ret = (int)bi; + if (ret >= minValue && ret <= maxValue) { + return ret; + } + } + throw new OverflowException("This object's value is out of range"); + } + + public object Negate(object obj) { + var ed = (EDecimal)obj; + return ed.Negate(); + } + + public object Abs(object obj) { + var ed = (EDecimal)obj; + return ed.Abs(); + } + + public ERational AsExtendedRational(object obj) { + return ERational.FromEDecimal((EDecimal)obj); + } + + public bool IsNegative(object obj) { + return ((EDecimal)obj).IsNegative; + } + } +} diff --git a/PeterO/Cbor/CBORExtendedFloat.cs b/PeterO/Cbor/CBORExtendedFloat.cs new file mode 100644 index 00000000..6e6906ac --- /dev/null +++ b/PeterO/Cbor/CBORExtendedFloat.cs @@ -0,0 +1,171 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBORExtendedFloat : ICBORNumber + { + public bool IsPositiveInfinity(object obj) { + var ef = (EFloat)obj; + return ef.IsPositiveInfinity(); + } + + public bool IsInfinity(object obj) { + var ef = (EFloat)obj; + return ef.IsInfinity(); + } + + public bool IsNegativeInfinity(object obj) { + var ef = (EFloat)obj; + return ef.IsNegativeInfinity(); + } + + public bool IsNaN(object obj) { + var ef = (EFloat)obj; + return ef.IsNaN(); + } + + public double AsDouble(object obj) { + var ef = (EFloat)obj; + return ef.ToDouble(); + } + + public EDecimal AsExtendedDecimal(object obj) { + var ef = (EFloat)obj; + return ef.ToEDecimal(); + } + + public EFloat AsExtendedFloat(object obj) { + var ef = (EFloat)obj; + return ef; + } + + public float AsSingle(object obj) { + var ef = (EFloat)obj; + return ef.ToSingle(); + } + + public EInteger AsEInteger(object obj) { + var ef = (EFloat)obj; + return ef.ToEInteger(); + } + + public long AsInt64(object obj) { + var ef = (EFloat)obj; + if (this.CanTruncatedIntFitInInt64(obj)) { + EInteger bi = ef.ToEInteger(); + return (long)bi; + } + throw new OverflowException("This object's value is out of range"); + } + + public bool CanFitInSingle(object obj) { + var ef = (EFloat)obj; + return (!ef.IsFinite) || + (ef.CompareTo(EFloat.FromSingle(ef.ToSingle())) == 0); + } + + public bool CanFitInDouble(object obj) { + var ef = (EFloat)obj; + return (!ef.IsFinite) || + (ef.CompareTo(EFloat.FromDouble(ef.ToDouble())) == 0); + } + + public bool CanFitInInt32(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt32(obj); + } + + public bool CanFitInInt64(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt64(obj); + } + + public bool CanTruncatedIntFitInInt64(object obj) { + var ef = (EFloat)obj; + if (!ef.IsFinite) { + return false; + } + if (ef.IsZero) { + return true; + } + if (ef.Exponent.CompareTo((EInteger)65) >= 0) { + return false; + } + EInteger bi = ef.ToEInteger(); + return bi.GetSignedBitLength() <= 63; + } + + public bool CanTruncatedIntFitInInt32(object obj) { + var ef = (EFloat)obj; + if (!ef.IsFinite) { + return false; + } + if (ef.IsZero) { + return true; + } + if (ef.Exponent.CompareTo((EInteger)33) >= 0) { + return false; + } + EInteger bi = ef.ToEInteger(); + return bi.CanFitInInt32(); + } + + public bool IsZero(object obj) { + var ef = (EFloat)obj; + return ef.IsZero; + } + + public int Sign(object obj) { + var ef = (EFloat)obj; + return ef.IsNaN() ? 2 : ef.Sign; + } + + public bool IsIntegral(object obj) { + var ef = (EFloat)obj; + if (!ef.IsFinite) { + return false; + } + if (ef.Exponent.Sign >= 0) { + return true; + } + EFloat ef2 = EFloat.FromEInteger(ef.ToEInteger()); + return ef2.CompareTo(ef) == 0; + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var ef = (EFloat)obj; + if (this.CanTruncatedIntFitInInt32(obj)) { + EInteger bi = ef.ToEInteger(); + var ret = (int)bi; + if (ret >= minValue && ret <= maxValue) { + return ret; + } + } + throw new OverflowException("This object's value is out of range"); + } + + public object Negate(object obj) { + var ed = (EFloat)obj; + return ed.Negate(); + } + + public object Abs(object obj) { + var ed = (EFloat)obj; + return ed.Abs(); + } + + public ERational AsExtendedRational(object obj) { + return ERational.FromEFloat((EFloat)obj); + } + + public bool IsNegative(object obj) { + return ((EFloat)obj).IsNegative; + } + } +} diff --git a/PeterO/Cbor/CBORExtendedRational.cs b/PeterO/Cbor/CBORExtendedRational.cs new file mode 100644 index 00000000..4f661313 --- /dev/null +++ b/PeterO/Cbor/CBORExtendedRational.cs @@ -0,0 +1,166 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBORExtendedRational : ICBORNumber + { + public bool IsPositiveInfinity(object obj) { + return ((ERational)obj).IsPositiveInfinity(); + } + + public bool IsInfinity(object obj) { + return ((ERational)obj).IsInfinity(); + } + + public bool IsNegativeInfinity(object obj) { + return ((ERational)obj).IsNegativeInfinity(); + } + + public bool IsNaN(object obj) { + return ((ERational)obj).IsNaN(); + } + + public double AsDouble(object obj) { + var er = (ERational)obj; + return er.ToDouble(); + } + + public EDecimal AsExtendedDecimal(object obj) { + var er = (ERational)obj; + return + + er.ToEDecimalExactIfPossible(EContext.Decimal128.WithUnlimitedExponents()); + } + + public EFloat AsExtendedFloat(object obj) { + var er = (ERational)obj; + return + + er.ToEFloatExactIfPossible(EContext.Binary128.WithUnlimitedExponents()); + } + + public float AsSingle(object obj) { + var er = (ERational)obj; + return er.ToSingle(); + } + + public EInteger AsEInteger(object obj) { + var er = (ERational)obj; + return er.ToEInteger(); + } + + public long AsInt64(object obj) { + var ef = (ERational)obj; + if (ef.IsFinite) { + EInteger bi = ef.ToEInteger(); + if (bi.GetSignedBitLength() <= 63) { + return (long)bi; + } + } + throw new OverflowException("This object's value is out of range"); + } + + public bool CanFitInSingle(object obj) { + var ef = (ERational)obj; + return (!ef.IsFinite) || + (ef.CompareTo(ERational.FromSingle(ef.ToSingle())) == 0); + } + + public bool CanFitInDouble(object obj) { + var ef = (ERational)obj; + return (!ef.IsFinite) || + (ef.CompareTo(ERational.FromDouble(ef.ToDouble())) == 0); + } + + public bool CanFitInInt32(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt32(obj); + } + + public bool CanFitInInt64(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt64(obj); + } + + public bool CanTruncatedIntFitInInt64(object obj) { + var ef = (ERational)obj; + if (!ef.IsFinite) { + return false; + } + EInteger bi = ef.ToEInteger(); + return bi.GetSignedBitLength() <= 63; + } + + public bool CanTruncatedIntFitInInt32(object obj) { + var ef = (ERational)obj; + if (!ef.IsFinite) { + return false; + } + EInteger bi = ef.ToEInteger(); + return bi.CanFitInInt32(); + } + + public bool IsZero(object obj) { + var ef = (ERational)obj; + return ef.IsZero; + } + + public int Sign(object obj) { + var ef = (ERational)obj; + return ef.Sign; + } + + public bool IsIntegral(object obj) { + var ef = (ERational)obj; + if (!ef.IsFinite) { + return false; + } + if (ef.Denominator.Equals(EInteger.One)) { + return true; + } + // A rational number is integral if the remainder + // of the numerator divided by the denominator is 0 + EInteger denom = ef.Denominator; + EInteger rem = ef.Numerator % (EInteger)denom; + return rem.IsZero; + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var ef = (ERational)obj; + if (ef.IsFinite) { + EInteger bi = ef.ToEInteger(); + if (bi.CanFitInInt32()) { + var ret = (int)bi; + if (ret >= minValue && ret <= maxValue) { + return ret; + } + } + } + throw new OverflowException("This object's value is out of range"); + } + + public object Negate(object obj) { + var ed = (ERational)obj; + return ed.Negate(); + } + + public object Abs(object obj) { + var ed = (ERational)obj; + return ed.Abs(); + } + + public ERational AsExtendedRational(object obj) { + return (ERational)obj; + } + + public bool IsNegative(object obj) { + return ((ERational)obj).IsNegative; + } + } +} diff --git a/PeterO/Cbor/CBORInteger.cs b/PeterO/Cbor/CBORInteger.cs new file mode 100644 index 00000000..af59d401 --- /dev/null +++ b/PeterO/Cbor/CBORInteger.cs @@ -0,0 +1,136 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CBORInteger : ICBORNumber { + public object Abs(object obj) { + var val = (long)obj; + return (val == Int32.MinValue) ? (EInteger.One << 63) : ((val < 0) ? + -val : obj); + } + + public EInteger AsEInteger(object obj) { + return (EInteger)(long)obj; + } + + public double AsDouble(object obj) { + return (double)(long)obj; + } + + public EDecimal AsExtendedDecimal(object obj) { + return EDecimal.FromInt64((long)obj); + } + + public EFloat AsExtendedFloat(object obj) { + return EFloat.FromInt64((long)obj); + } + + public ERational AsExtendedRational(object obj) { + return ERational.FromInt64((long)obj); + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var val = (long)obj; + if (val >= minValue && val <= maxValue) { + return (int)val; + } + throw new OverflowException("This object's value is out of range"); + } + + public long AsInt64(object obj) { + return (long)obj; + } + + public float AsSingle(object obj) { + return (float)(long)obj; + } + + public bool CanFitInDouble(object obj) { + var intItem = (long)obj; + if (intItem == Int64.MinValue) { + return true; + } + intItem = (intItem < 0) ? -intItem : intItem; + while (intItem >= (1L << 53) && (intItem & 1) == 0) { + intItem >>= 1; + } + return intItem < (1L << 53); + } + + public bool CanFitInInt32(object obj) { + var val = (long)obj; + return val >= Int32.MinValue && val <= Int32.MaxValue; + } + + public bool CanFitInInt64(object obj) { + return true; + } + + public bool CanFitInSingle(object obj) { + var intItem = (long)obj; + if (intItem == Int64.MinValue) { + return true; + } + intItem = (intItem < 0) ? -intItem : intItem; + while (intItem >= (1L << 24) && (intItem & 1) == 0) { + intItem >>= 1; + } + return intItem < (1L << 24); + } + + public bool CanTruncatedIntFitInInt32(object obj) { + var val = (long)obj; + return val >= Int32.MinValue && val <= Int32.MaxValue; + } + + public bool CanTruncatedIntFitInInt64(object obj) { + return true; + } + + public bool IsInfinity(object obj) { + return false; + } + + public bool IsIntegral(object obj) { + return true; + } + + public bool IsNaN(object obj) { + return false; + } + + public bool IsNegative(object obj) { + return ((long)obj) < 0; + } + + public bool IsNegativeInfinity(object obj) { + return false; + } + + public bool IsPositiveInfinity(object obj) { + return false; + } + + public bool IsZero(object obj) { + return ((long)obj) == 0; + } + + public object Negate(object obj) { + return (((long)obj) == Int64.MinValue) ? (EInteger.One << 63) : + (-((long)obj)); + } + + public int Sign(object obj) { + var val = (long)obj; + return (val == 0) ? 0 : ((val < 0) ? -1 : 1); + } + } +} diff --git a/PeterO/Cbor/CBORJson.cs b/PeterO/Cbor/CBORJson.cs new file mode 100644 index 00000000..4f2a99e5 --- /dev/null +++ b/PeterO/Cbor/CBORJson.cs @@ -0,0 +1,730 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Collections.Generic; +using System.Text; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal sealed class CBORJson { + // JSON parsing methods + private static int SkipWhitespaceJSON(CharacterInputWithCount reader) { + while (true) { + int c = reader.ReadChar(); + if (c == -1 || (c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)) { + return c; + } + } + } + + private CharacterInputWithCount reader; + private StringBuilder sb; + + private string NextJSONString() { + int c; + this.sb = this.sb ?? new StringBuilder(); + this.sb.Remove(0, this.sb.Length); + while (true) { + c = this.reader.ReadChar(); + if (c == -1 || c < 0x20) { + this.reader.RaiseError("Unterminated string"); + } + switch (c) { + case '\\': + c = this.reader.ReadChar(); + switch (c) { + case '\\': + this.sb.Append('\\'); + break; + case '/': + // Now allowed to be escaped under RFC 8259 + this.sb.Append('/'); + break; + case '\"': + this.sb.Append('\"'); + break; + case 'b': + this.sb.Append('\b'); + break; + case 'f': + this.sb.Append('\f'); + break; + case 'n': + this.sb.Append('\n'); + break; + case 'r': + this.sb.Append('\r'); + break; + case 't': + this.sb.Append('\t'); + break; + case 'u': { // Unicode escape + c = 0; + // Consists of 4 hex digits + for (var i = 0; i < 4; ++i) { + int ch = this.reader.ReadChar(); + if (ch >= '0' && ch <= '9') { + c <<= 4; + c |= ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + c <<= 4; + c |= ch + 10 - 'A'; + } else if (ch >= 'a' && ch <= 'f') { + c <<= 4; + c |= ch + 10 - 'a'; + } else { + this.reader.RaiseError("Invalid Unicode escaped character"); + } + } + if ((c & 0xf800) != 0xd800) { + // Non-surrogate + this.sb.Append((char)c); + } else if ((c & 0xfc00) == 0xd800) { + int ch = this.reader.ReadChar(); + if (ch != '\\' || this.reader.ReadChar() != 'u') { + this.reader.RaiseError("Invalid escaped character"); + } + var c2 = 0; + for (var i = 0; i < 4; ++i) { + ch = this.reader.ReadChar(); + if (ch >= '0' && ch <= '9') { + c2 <<= 4; + c2 |= ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + c2 <<= 4; + c2 |= ch + 10 - 'A'; + } else if (ch >= 'a' && ch <= 'f') { + c2 <<= 4; + c2 |= ch + 10 - 'a'; + } else { + this.reader.RaiseError("Invalid Unicode escaped character"); + } + } + if ((c2 & 0xfc00) != 0xdc00) { + this.reader.RaiseError("Unpaired surrogate code point"); + } else { + this.sb.Append((char)c); + this.sb.Append((char)c2); + } + } else { + this.reader.RaiseError("Unpaired surrogate code point"); + } + break; + } + default: + { + this.reader.RaiseError("Invalid escaped character"); + break; + } + } + break; + case 0x22: // double quote + return this.sb.ToString(); + default: { + // NOTE: Assumes the character reader + // throws an error on finding illegal surrogate + // pairs in the string or invalid encoding + // in the stream + if ((c >> 16) == 0) { + this.sb.Append((char)c); + } else { + this.sb.Append((char)((((c - 0x10000) >> 10) & 0x3ff) + + 0xd800)); + this.sb.Append((char)(((c - 0x10000) & 0x3ff) + 0xdc00)); + } + break; + } + } + } + } + + private CBORObject NextJSONValue( + int firstChar, + int[] nextChar, + int depth) { + string str; + int c = firstChar; + CBORObject obj = null; + if (c < 0) { + this.reader.RaiseError("Unexpected end of data"); + } + switch (c) { + case '"': + { + // Parse a string + // The tokenizer already checked the string for invalid + // surrogate pairs, so just call the CBORObject + // constructor directly + obj = CBORObject.FromRaw(this.NextJSONString()); + nextChar[0] = SkipWhitespaceJSON(this.reader); + return obj; + } + case '{': + { + // Parse an object + obj = this.ParseJSONObject(depth + 1); + nextChar[0] = SkipWhitespaceJSON(this.reader); + return obj; + } + case '[': + { + // Parse an array + obj = this.ParseJSONArray(depth + 1); + nextChar[0] = SkipWhitespaceJSON(this.reader); + return obj; + } + case 't': + { + // Parse true + if (this.reader.ReadChar() != 'r' || this.reader.ReadChar() != 'u' || + this.reader.ReadChar() != 'e') { + this.reader.RaiseError("Value can't be parsed."); + } + nextChar[0] = SkipWhitespaceJSON(this.reader); + return CBORObject.True; + } + case 'f': + { + // Parse false + if (this.reader.ReadChar() != 'a' || this.reader.ReadChar() != 'l' || + this.reader.ReadChar() != 's' || this.reader.ReadChar() != 'e') { + this.reader.RaiseError("Value can't be parsed."); + } + nextChar[0] = SkipWhitespaceJSON(this.reader); + return CBORObject.False; + } + case 'n': + { + // Parse null + if (this.reader.ReadChar() != 'u' || this.reader.ReadChar() != 'l' || + this.reader.ReadChar() != 'l') { + this.reader.RaiseError("Value can't be parsed."); + } + nextChar[0] = SkipWhitespaceJSON(this.reader); + return CBORObject.Null; + } + case '-': + { + // Parse a negative number + var lengthTwo = true; + c = this.reader.ReadChar(); + if (c < '0' || c > '9') { + this.reader.RaiseError("JSON number can't be parsed."); + } + int cval = -(c - '0'); + int cstart = c; + StringBuilder sb = null; + c = this.reader.ReadChar(); + while (c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9') || + c == 'e' || c == 'E') { + if (lengthTwo) { + sb = new StringBuilder(); + sb.Append((char)'-'); + sb.Append((char)cstart); + lengthTwo = false; + } + sb.Append((char)c); + c = this.reader.ReadChar(); + } + if (lengthTwo) { + obj = cval == 0 ? + CBORDataUtilities.ParseJSONNumber("-0", true, false, true) : + CBORObject.FromObject(cval); + } else { + str = sb.ToString(); + obj = CBORDataUtilities.ParseJSONNumber(str); + if (obj == null) { + this.reader.RaiseError("JSON number can't be parsed. " + str); + } + } + if (c == -1 || (c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)) { + nextChar[0] = c; + } else { + nextChar[0] = SkipWhitespaceJSON(this.reader); + } + return obj; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + // Parse a number + var lengthOne = true; + int cval = c - '0'; + int cstart = c; + StringBuilder sb = null; + c = this.reader.ReadChar(); + while (c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9') || + c == 'e' || c == 'E') { + if (lengthOne) { + sb = new StringBuilder(); + sb.Append((char)cstart); + lengthOne = false; + } + sb.Append((char)c); + c = this.reader.ReadChar(); + } + if (lengthOne) { + obj = CBORObject.FromObject(cval); + } else { + str = sb.ToString(); + obj = CBORDataUtilities.ParseJSONNumber(str); + if (obj == null) { + this.reader.RaiseError("JSON number can't be parsed. " + str); + } + } + if (c == -1 || (c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)) { + nextChar[0] = c; + } else { + nextChar[0] = SkipWhitespaceJSON(this.reader); + } + return obj; + } + default: + this.reader.RaiseError("Value can't be parsed."); + break; + } + return null; + } + + private bool noDuplicates; + + public CBORJson(CharacterInputWithCount reader, bool noDuplicates) { + this.reader = reader; + this.sb = null; + this.noDuplicates = noDuplicates; + } + + public CBORObject ParseJSON(bool objectOrArrayOnly, int[] nextchar) { + int c; + CBORObject ret; + c = SkipWhitespaceJSON(this.reader); + if (c == '[') { + ret = this.ParseJSONArray(0); + nextchar[0] = SkipWhitespaceJSON(this.reader); + return ret; + } + if (c == '{') { + ret = this.ParseJSONObject(0); + nextchar[0] = SkipWhitespaceJSON(this.reader); + return ret; + } + if (objectOrArrayOnly) { + this.reader.RaiseError("A JSON object must begin with '{' or '['"); + } + return this.NextJSONValue(c, nextchar, 0); + } + + internal static CBORObject ParseJSONValue( + CharacterInputWithCount reader, + bool noDuplicates, + bool objectOrArrayOnly, + int[] nextchar) { + var cj = new CBORJson(reader, noDuplicates); + return cj.ParseJSON(objectOrArrayOnly, nextchar); + } + + private CBORObject ParseJSONObject(int depth) { + // Assumes that the last character read was '{' + if (depth > 1000) { + this.reader.RaiseError("Too deeply nested"); + } + int c; + CBORObject key = null; + CBORObject obj; + var nextchar = new int[1]; + var seenComma = false; + var myHashMap = new Dictionary(); + while (true) { + c = SkipWhitespaceJSON(this.reader); + switch (c) { + case -1: + this.reader.RaiseError("A JSONObject must end with '}'"); + break; + case '}': + if (seenComma) { + // Situation like '{"0"=>1,}' + this.reader.RaiseError("Trailing comma"); + return null; + } + return CBORObject.FromRaw(myHashMap); + default: { + // Read the next string + if (c < 0) { + this.reader.RaiseError("Unexpected end of data"); + return null; + } + if (c != '"') { + this.reader.RaiseError("Expected a string as a key"); + return null; + } + // Parse a string that represents the object's key + // The tokenizer already checked the string for invalid + // surrogate pairs, so just call the CBORObject + // constructor directly + obj = CBORObject.FromRaw(this.NextJSONString()); + key = obj; + if (this.noDuplicates && myHashMap.ContainsKey(obj)) { + this.reader.RaiseError("Key already exists: " + key); + return null; + } + break; + } + } + if (SkipWhitespaceJSON(this.reader) != ':') { + this.reader.RaiseError("Expected a ':' after a key"); + } + // NOTE: Will overwrite existing value + myHashMap[key] = this.NextJSONValue( + SkipWhitespaceJSON(this.reader), + nextchar, + depth); + switch (nextchar[0]) { + case ',': + seenComma = true; + break; + case '}': + return CBORObject.FromRaw(myHashMap); + default: this.reader.RaiseError("Expected a ',' or '}'"); + break; + } + } + } + + internal CBORObject ParseJSONArray(int depth) { + // Assumes that the last character read was '[' + if (depth > 1000) { + this.reader.RaiseError("Too deeply nested"); + } + var myArrayList = new List(); + var seenComma = false; + var nextchar = new int[1]; + while (true) { + int c = SkipWhitespaceJSON(this.reader); + if (c == ']') { + if (seenComma) { + // Situation like '[0,1,]' + this.reader.RaiseError("Trailing comma"); + } + return CBORObject.FromRaw(myArrayList); + } + if (c == ',') { + // Situation like '[,0,1,2]' or '[0,,1]' + this.reader.RaiseError("Empty array element"); + } + myArrayList.Add( + this.NextJSONValue( + c, + nextchar, + depth)); + c = nextchar[0]; + switch (c) { + case ',': + seenComma = true; + break; + case ']': + return CBORObject.FromRaw(myArrayList); + default: + this.reader.RaiseError("Expected a ',' or ']'"); + break; + } + } + } + + private const string Hex16 = "0123456789ABCDEF"; + + internal static void WriteJSONStringUnquoted( + string str, + StringOutput sb) { + // Surrogates were already verified when this + // string was added to the CBOR object; that check + // is not repeated here + var first = true; + for (var i = 0; i < str.Length; ++i) { + char c = str[i]; + if (c == '\\' || c == '"') { + if (first) { + first = false; + sb.WriteString(str, 0, i); + } + sb.WriteCodePoint((int)'\\'); + sb.WriteCodePoint((int)c); + } else if (c < 0x20 || (c >= 0x7f && (c == 0x2028 || c == 0x2029 || + (c >= 0x7f && c <= 0xa0) || c == 0xfeff || c == 0xfffe || + c == 0xffff))) { + // Control characters, and also the line and paragraph separators + // which apparently can't appear in JavaScript (as opposed to + // JSON) strings + if (first) { + first = false; + sb.WriteString(str, 0, i); + } + if (c == 0x0d) { + sb.WriteString("\\r"); + } else if (c == 0x0a) { + sb.WriteString("\\n"); + } else if (c == 0x08) { + sb.WriteString("\\b"); + } else if (c == 0x0c) { + sb.WriteString("\\f"); + } else if (c == 0x09) { + sb.WriteString("\\t"); + } else if (c == 0x85) { + sb.WriteString("\\u0085"); + } else if (c >= 0x100) { + sb.WriteString("\\u"); + sb.WriteCodePoint((int)Hex16[(int)((c >> 12) & 15)]); + sb.WriteCodePoint((int)Hex16[(int)((c >> 8) & 15)]); + sb.WriteCodePoint((int)Hex16[(int)((c >> 4) & 15)]); + sb.WriteCodePoint((int)Hex16[(int)(c & 15)]); + } else { + sb.WriteString("\\u00"); + sb.WriteCodePoint((int)Hex16[(int)(c >> 4)]); + sb.WriteCodePoint((int)Hex16[(int)(c & 15)]); + } + } else if (!first) { + if ((c & 0xfc00) == 0xd800) { + sb.WriteString(str, i, 2); + ++i; + } else { + sb.WriteCodePoint((int)c); + } + } + } + if (first) { + sb.WriteString(str); + } + } + + internal static void WriteJSONToInternal( + CBORObject obj, + StringOutput writer, + JSONOptions options) { + int type = obj.ItemType; + object thisItem = obj.ThisItem; + switch (type) { + case CBORObject.CBORObjectTypeSimpleValue: { + if (obj.IsTrue) { + writer.WriteString("true"); + return; + } + if (obj.IsFalse) { + writer.WriteString("false"); + return; + } + writer.WriteString("null"); + return; + } + case CBORObject.CBORObjectTypeSingle: { + var f = (float)thisItem; + if (Single.IsNegativeInfinity(f) || + Single.IsPositiveInfinity(f) || Single.IsNaN(f)) { + writer.WriteString("null"); + return; + } + writer.WriteString( + CBORObject.TrimDotZero( + CBORUtilities.SingleToString(f))); + return; + } + case CBORObject.CBORObjectTypeDouble: { + var f = (double)thisItem; + if (Double.IsNegativeInfinity(f) || Double.IsPositiveInfinity(f) || + Double.IsNaN(f)) { + writer.WriteString("null"); + return; + } + string dblString = CBORUtilities.DoubleToString(f); + writer.WriteString( + CBORObject.TrimDotZero(dblString)); + return; + } + case CBORObject.CBORObjectTypeInteger: { + var longItem = (long)thisItem; + writer.WriteString(CBORUtilities.LongToString(longItem)); + return; + } + case CBORObject.CBORObjectTypeBigInteger: { + writer.WriteString(((EInteger)thisItem).ToString()); + return; + } + case CBORObject.CBORObjectTypeExtendedDecimal: { + var dec = (EDecimal)thisItem; + if (dec.IsInfinity() || dec.IsNaN()) { + writer.WriteString("null"); + } else { + writer.WriteString(dec.ToString()); + } + return; + } + case CBORObject.CBORObjectTypeExtendedFloat: { + var flo = (EFloat)thisItem; + if (flo.IsInfinity() || flo.IsNaN()) { + writer.WriteString("null"); + return; + } + if (flo.IsFinite && + flo.Exponent.Abs().CompareTo((EInteger)2500) > 0) { + // Too inefficient to convert to a decimal number + // from a bigfloat with a very high exponent, + // so convert to double instead + double f = flo.ToDouble(); + if (Double.IsNegativeInfinity(f) || + Double.IsPositiveInfinity(f) || Double.IsNaN(f)) { + writer.WriteString("null"); + return; + } + string dblString = + CBORUtilities.DoubleToString(f); + writer.WriteString( + CBORObject.TrimDotZero(dblString)); + return; + } + writer.WriteString(flo.ToString()); + return; + } + case CBORObject.CBORObjectTypeByteString: + { + var byteArray = (byte[])thisItem; + if (byteArray.Length == 0) { + writer.WriteString("\"\""); + return; + } + writer.WriteCodePoint((int)'\"'); + if (obj.HasTag(22)) { + Base64.WriteBase64( + writer, + byteArray, + 0, + byteArray.Length, + options.Base64Padding); + } else if (obj.HasTag(23)) { + // Write as base16 + for (int i = 0; i < byteArray.Length; ++i) { + writer.WriteCodePoint((int)Hex16[(byteArray[i] >> 4) & 15]); + writer.WriteCodePoint((int)Hex16[byteArray[i] & 15]); + } + } else { + Base64.WriteBase64URL( + writer, + byteArray, + 0, + byteArray.Length, + options.Base64Padding); + } + writer.WriteCodePoint((int)'\"'); + break; + } + case CBORObject.CBORObjectTypeTextString: { + var thisString = (string)thisItem; + if (thisString.Length == 0) { + writer.WriteString("\"\""); + return; + } + writer.WriteCodePoint((int)'\"'); + WriteJSONStringUnquoted(thisString, writer); + writer.WriteCodePoint((int)'\"'); + break; + } + case CBORObject.CBORObjectTypeArray: { + var first = true; + writer.WriteCodePoint((int)'['); + foreach (CBORObject i in obj.AsList()) { + if (!first) { + writer.WriteCodePoint((int)','); + } + WriteJSONToInternal(i, writer, options); + first = false; + } + writer.WriteCodePoint((int)']'); + break; + } + case CBORObject.CBORObjectTypeExtendedRational: { + var dec = (ERational)thisItem; + EDecimal f = dec.ToEDecimalExactIfPossible( + EContext.Decimal128.WithUnlimitedExponents()); + if (!f.IsFinite) { + writer.WriteString("null"); + } else { + writer.WriteString(f.ToString()); + } + break; + } + case CBORObject.CBORObjectTypeMap: { + var first = true; + var hasNonStringKeys = false; + IDictionary objMap = obj.AsMap(); + foreach (KeyValuePair entry in objMap) { + CBORObject key = entry.Key; + if (key.ItemType != CBORObject.CBORObjectTypeTextString) { + hasNonStringKeys = true; + break; + } + } + if (!hasNonStringKeys) { + writer.WriteCodePoint((int)'{'); + foreach (KeyValuePair entry in objMap) { + CBORObject key = entry.Key; + CBORObject value = entry.Value; + if (!first) { + writer.WriteCodePoint((int)','); + } + writer.WriteCodePoint((int)'\"'); + WriteJSONStringUnquoted((string)key.ThisItem, writer); + writer.WriteCodePoint((int)'\"'); + writer.WriteCodePoint((int)':'); + WriteJSONToInternal(value, writer, options); + first = false; + } + writer.WriteCodePoint((int)'}'); + } else { + // This map has non-string keys + IDictionary stringMap = new + Dictionary(); + // Copy to a map with String keys, since + // some keys could be duplicates + // when serialized to strings + foreach (KeyValuePair entry in objMap) { + CBORObject key = entry.Key; + CBORObject value = entry.Value; + string str = (key.ItemType == CBORObject.CBORObjectTypeTextString) ? + ((string)key.ThisItem) : key.ToJSONString(); + stringMap[str] = value; + } + first = true; + writer.WriteCodePoint((int)'{'); + foreach (KeyValuePair entry in stringMap) { + string key = entry.Key; + CBORObject value = entry.Value; + if (!first) { + writer.WriteCodePoint((int)','); + } + writer.WriteCodePoint((int)'\"'); + WriteJSONStringUnquoted((string)key, writer); + writer.WriteCodePoint((int)'\"'); + writer.WriteCodePoint((int)':'); + WriteJSONToInternal(value, writer, options); + first = false; + } + writer.WriteCodePoint((int)'}'); + } + break; + } + default: throw new InvalidOperationException("Unexpected item type"); + } + } + } +} diff --git a/PeterO/Cbor/CBORObject.cs b/PeterO/Cbor/CBORObject.cs new file mode 100644 index 00000000..1f401452 --- /dev/null +++ b/PeterO/Cbor/CBORObject.cs @@ -0,0 +1,4304 @@ +/* +Written by Peter O. in 2013-2018. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + /// + public sealed partial class CBORObject : IComparable, + IEquatable { + private static CBORObject ConstructSimpleValue(int v) { + return new CBORObject(CBORObjectTypeSimpleValue, v); + } + + private static CBORObject ConstructIntegerValue(int v) { + return new CBORObject(CBORObjectTypeInteger, (long)v); + } + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "This CBORObject is immutable")] +#endif + + public static readonly CBORObject False = + CBORObject.ConstructSimpleValue(20); + + /// + public static readonly CBORObject NaN = CBORObject.FromObject(Double.NaN); + + /// + public static readonly CBORObject NegativeInfinity = + CBORObject.FromObject(Double.NegativeInfinity); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "This CBORObject is immutable")] +#endif + + public static readonly CBORObject Null = + CBORObject.ConstructSimpleValue(22); + + /// + public static readonly CBORObject PositiveInfinity = + CBORObject.FromObject(Double.PositiveInfinity); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "This CBORObject is immutable")] +#endif + + public static readonly CBORObject True = + CBORObject.ConstructSimpleValue(21); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "This CBORObject is immutable")] +#endif + + public static readonly CBORObject Undefined = + CBORObject.ConstructSimpleValue(23); + + /// + public static readonly CBORObject Zero = + CBORObject.ConstructIntegerValue(0); + + internal const int CBORObjectTypeArray = 4; + internal const int CBORObjectTypeBigInteger = 1; // all other integers + internal const int CBORObjectTypeByteString = 2; + internal const int CBORObjectTypeDouble = 8; + internal const int CBORObjectTypeExtendedDecimal = 9; + internal const int CBORObjectTypeExtendedFloat = 11; + internal const int CBORObjectTypeExtendedRational = 12; + internal const int CBORObjectTypeInteger = 0; // -(2^63).. (2^63-1) + internal const int CBORObjectTypeMap = 5; + internal const int CBORObjectTypeSimpleValue = 6; + internal const int CBORObjectTypeSingle = 7; + internal const int CBORObjectTypeTagged = 10; + internal const int CBORObjectTypeTextString = 3; + internal static readonly EInteger Int64MaxValue = + (EInteger)Int64.MaxValue; + + internal static readonly EInteger Int64MinValue = + (EInteger)Int64.MinValue; + + private const int StreamedStringBufferLength = 4096; + + private static readonly IDictionary + ValueConverters = new Dictionary(); + + private static readonly ICBORNumber[] NumberInterfaces = { + new CBORInteger(), new CBOREInteger(), null, null, + null, null, null, new CBORSingle(), + new CBORDouble(), new CBORExtendedDecimal(), + null, new CBORExtendedFloat(), new CBORExtendedRational() + }; + + #pragma warning disable 618 + private static readonly IDictionary + ValueTagHandlers = new Dictionary(); + #pragma warning restore 618 + + private static readonly EInteger UInt64MaxValue = + (EInteger.One << 64) - EInteger.One; + + private static readonly EInteger[] ValueEmptyTags = new EInteger[0]; + // Expected lengths for each head byte. + // 0 means length varies. -1 means invalid. + private static readonly int[] ValueExpectedLengths = { 1, 1, 1, 1, 1, 1, + 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, // major type 0 + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 5, 9, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // major type 1 + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 5, 9, -1, -1, -1, -1, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // major type 2 + 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, -1, -1, -1, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // major type 3 + 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, -1, -1, -1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // major type 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // major type 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // major type 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // major type 7 + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 5, 9, -1, -1, -1, -1 }; + + private static readonly byte[] ValueFalseBytes = { 0x66, 0x61, 0x6c, + 0x73, 0x65 }; + + private static readonly byte[] ValueNullBytes = { 0x6e, 0x75, 0x6c, 0x6c }; + + private static readonly int[] ValueNumberTypeOrder = { 0, 0, 2, 3, 4, 5, + 1, 0, 0, + 0, 0, 0, 0 }; + + private static readonly byte[] ValueTrueBytes = { 0x74, 0x72, 0x75, 0x65 }; + + private static CBORObject[] valueFixedObjects = InitializeFixedObjects(); + + private readonly int itemtypeValue; + private readonly object itemValue; + private readonly int tagHigh; + private readonly int tagLow; + + internal CBORObject(CBORObject obj, int tagLow, int tagHigh) { + this.itemtypeValue = CBORObjectTypeTagged; + this.itemValue = obj; + this.tagLow = tagLow; + this.tagHigh = tagHigh; + } + + internal CBORObject(int type, object item) { +#if DEBUG + // Check range in debug mode to ensure that Integer and EInteger + // are unambiguous + if ((type == CBORObjectTypeBigInteger) && + ((EInteger)item).CompareTo(Int64MinValue) >= 0 && + ((EInteger)item).CompareTo(Int64MaxValue) <= 0) { + throw new ArgumentException("Big integer is within range for Integer"); + } + if (type == CBORObjectTypeArray && !(item is IList)) { + throw new InvalidOperationException(); + } +#endif + this.itemtypeValue = type; + this.itemValue = item; + this.tagLow = 0; + this.tagHigh = 0; + } + + /// + public int Count { + get { + return (this.ItemType == CBORObjectTypeArray) ? this.AsList().Count : + ((this.ItemType == CBORObjectTypeMap) ? this.AsMap().Count : 0); + } + } + + /// + [Obsolete("Use MostInnerTag instead.")] + public BigInteger InnermostTag { + get { + EInteger ei = this.MostInnerTag; + String eis = ei.ToString(); + return BigInteger.fromString(eis); + } + } + + /// + [Obsolete("Use MostOuterTag instead.")] + public BigInteger OutermostTag { + get { + EInteger ei = this.MostOuterTag; + return BigInteger.fromString(this.MostOuterTag.ToString()); + } + } + + /// + public EInteger MostInnerTag { + get { + if (!this.IsTagged) { + return EInteger.FromInt32(-1); + } + CBORObject previtem = this; + var curitem = (CBORObject)this.itemValue; + while (curitem.IsTagged) { + previtem = curitem; + curitem = (CBORObject)curitem.itemValue; + } + if (previtem.tagHigh == 0 && previtem.tagLow >= 0 && + previtem.tagLow < 0x10000) { + return (EInteger)previtem.tagLow; + } + return LowHighToEInteger( + previtem.tagLow, + previtem.tagHigh); + } + } + + /// + public bool IsFalse { + get { + return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem + == 20; + } + } + + /// + public bool IsFinite { + get { + return this.Type == CBORType.Number && !this.IsInfinity() && + !this.IsNaN(); + } + } + + /// + public bool IsIntegral { + get { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return (cn != null) && cn.IsIntegral(this.ThisItem); + } + } + + /// + public bool IsNull { + get { + return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem + == 22; + } + } + + /// + public bool IsTagged { + get { + return this.itemtypeValue == CBORObjectTypeTagged; + } + } + + /// + public bool IsTrue { + get { + return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem + == 21; + } + } + + /// + public bool IsUndefined { + get { + return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem + == 23; + } + } + + /// + public bool IsZero { + get { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return cn != null && cn.IsZero(this.ThisItem); + } + } + + /// + public ICollection Keys { + get { + if (this.ItemType == CBORObjectTypeMap) { + IDictionary dict = this.AsMap(); + return dict.Keys; + } + throw new InvalidOperationException("Not a map"); + } + } + + /// + public bool IsNegative { + get { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return (cn != null) && cn.IsNegative(this.ThisItem); + } + } + + /// + public EInteger MostOuterTag { + get { + if (!this.IsTagged) { + return EInteger.FromInt32(-1); + } + if (this.tagHigh == 0 && + this.tagLow >= 0 && this.tagLow < 0x10000) { + return (EInteger)this.tagLow; + } + return LowHighToEInteger( + this.tagLow, + this.tagHigh); + } + } + + /// + public int Sign { + get { + int ret = GetSignInternal(this.ItemType, this.ThisItem); + if (ret == 2) { + throw new InvalidOperationException("This object is not a number."); + } + return ret; + } + } + + /// + public int SimpleValue { + get { + return (this.ItemType == CBORObjectTypeSimpleValue) ? + ((int)this.ThisItem) : (-1); + } + } + + /// + public CBORType Type { + get { + switch (this.ItemType) { + case CBORObjectTypeInteger: + case CBORObjectTypeBigInteger: + case CBORObjectTypeSingle: + case CBORObjectTypeDouble: + case CBORObjectTypeExtendedDecimal: + case CBORObjectTypeExtendedFloat: + case CBORObjectTypeExtendedRational: + return CBORType.Number; + case CBORObjectTypeSimpleValue: + return ((int)this.ThisItem == 21 || (int)this.ThisItem == 20) ? + CBORType.Boolean : CBORType.SimpleValue; + case CBORObjectTypeArray: + return CBORType.Array; + case CBORObjectTypeMap: + return CBORType.Map; + case CBORObjectTypeByteString: + return CBORType.ByteString; + case CBORObjectTypeTextString: + return CBORType.TextString; + default: + throw new InvalidOperationException("Unexpected data type"); + } + } + } + + /// + public ICollection Values { + get { + if (this.ItemType == CBORObjectTypeMap) { + IDictionary dict = this.AsMap(); + return dict.Values; + } + if (this.ItemType == CBORObjectTypeArray) { + IList list = this.AsList(); + return new + System.Collections.ObjectModel.ReadOnlyCollection(list); + } + throw new InvalidOperationException("Not a map or array"); + } + } + + internal int ItemType { + get { + CBORObject curobject = this; + while (curobject.itemtypeValue == CBORObjectTypeTagged) { + curobject = (CBORObject)curobject.itemValue; + } + return curobject.itemtypeValue; + } + } + + internal object ThisItem { + get { + CBORObject curobject = this; + while (curobject.itemtypeValue == CBORObjectTypeTagged) { + curobject = (CBORObject)curobject.itemValue; + } + return curobject.itemValue; + } + } + + /// + public CBORObject this[int index] { + get { + if (this.ItemType == CBORObjectTypeArray) { + IList list = this.AsList(); + if (index < 0 || index >= list.Count) { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return list[index]; + } + throw new InvalidOperationException("Not an array"); + } + + set { + if (this.ItemType == CBORObjectTypeArray) { + if (value == null) { + throw new ArgumentNullException(nameof(value)); + } + IList list = this.AsList(); + list[index] = value; + } else { + throw new InvalidOperationException("Not an array"); + } + } + } + + /// + public CBORObject this[CBORObject key] { + get { + if (key == null) { + throw new ArgumentNullException(nameof(key)); + } + if (this.ItemType == CBORObjectTypeMap) { + IDictionary map = this.AsMap(); + return (!map.ContainsKey(key)) ? null : map[key]; + } + throw new InvalidOperationException("Not a map"); + } + + set { + if (key == null) { + throw new ArgumentNullException(nameof(value)); + } + if (value == null) { + throw new ArgumentNullException(nameof(value)); + } + if (this.ItemType == CBORObjectTypeMap) { + IDictionary map = this.AsMap(); + map[key] = value; + } else { + throw new InvalidOperationException("Not a map"); + } + } + } + + /// + public CBORObject this[string key] { + get { + if (key == null) { + throw new ArgumentNullException(nameof(key)); + } + CBORObject objkey = CBORObject.FromObject(key); + return this[objkey]; + } + + set { + if (key == null) { + throw new ArgumentNullException(nameof(value)); + } + if (value == null) { + throw new ArgumentNullException(nameof(value)); + } + CBORObject objkey = CBORObject.FromObject(key); + if (this.ItemType == CBORObjectTypeMap) { + IDictionary map = this.AsMap(); + map[objkey] = value; + } else { + throw new InvalidOperationException("Not a map"); + } + } + } + + /// + [Obsolete("To be replaced with the AddConverter method of CBORTypeMapper.")] + public static void AddConverter(Type type, ICBORConverter converter) { + if (type == null) { + throw new ArgumentNullException(nameof(type)); + } + if (converter == null) { + throw new ArgumentNullException(nameof(converter)); + } + ConverterInfo ci = new CBORObject.ConverterInfo(); + ci.Converter = converter; + ci.ToObject = PropertyMap.FindOneArgumentMethod( + converter, + "ToCBORObject", + type); + if (ci.ToObject == null) { + throw new ArgumentException( + "Converter doesn't contain a proper ToCBORObject method"); + } + lock (ValueConverters) { + ValueConverters[type] = ci; + } + } + + /// + public static CBORObject Addition(CBORObject first, CBORObject second) { + return CBORObjectMath.Addition(first, second); + } + + /// + [Obsolete("May be removed in the future without replacement. Not as useful as ICBORConverters and ICBORObjectConverters for FromObject and ToObject. Moreover, registering tag handlers as this method does may tie them to the lifetime of the application.")] + public static void AddTagHandler(BigInteger bigintTag, ICBORTag handler) { + if (bigintTag == null) { + throw new ArgumentNullException(nameof(bigintTag)); + } + if (handler == null) { + throw new ArgumentNullException(nameof(handler)); + } + AddTagHandler(PropertyMap.FromLegacy(bigintTag), handler); + } + + /// + [Obsolete("May be removed in the future without replacement. Not as useful as ICBORConverters and ICBORObjectConverters for FromObject and ToObject. Moreover, registering tag handlers as this method does may tie them to the lifetime of the application.")] + public static void AddTagHandler(EInteger bigintTag, ICBORTag handler) { + if (bigintTag == null) { + throw new ArgumentNullException(nameof(bigintTag)); + } + if (handler == null) { + throw new ArgumentNullException(nameof(handler)); + } + if (bigintTag.Sign < 0) { + throw new ArgumentException("bigintTag.Sign (" + + bigintTag.Sign + ") is less than 0"); + } + if (bigintTag.GetSignedBitLength() > 64) { + throw new ArgumentException("bigintTag.bitLength (" + + (long)bigintTag.GetSignedBitLength() + ") is more than " + + "64"); + } + lock (ValueTagHandlers) { + ValueTagHandlers[bigintTag] = handler; + } + } + + /// + public static CBORObject DecodeFromBytes(byte[] data) { + return DecodeFromBytes(data, new CBOREncodeOptions(true, true)); + } + + /// + public static CBORObject DecodeFromBytes( + byte[] data, + CBOREncodeOptions options) { + if (data == null) { + throw new ArgumentNullException(nameof(data)); + } + if (data.Length == 0) { + throw new CBORException("data is empty."); + } + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + var firstbyte = (int)(data[0] & (int)0xff); + int expectedLength = ValueExpectedLengths[firstbyte]; + // if invalid + if (expectedLength == -1) { + throw new CBORException("Unexpected data encountered"); + } + if (expectedLength != 0) { + // if fixed length + CheckCBORLength(expectedLength, data.Length); + } + if (firstbyte == 0xc0) { + // value with tag 0 + string s = GetOptimizedStringIfShortAscii(data, 1); + if (s != null) { + return new CBORObject(FromObject(s), 0, 0); + } + } + if (expectedLength != 0) { + return GetFixedLengthObject(firstbyte, data); + } + // For objects with variable length, + // read the object as though + // the byte array were a stream + using (var ms = new MemoryStream(data)) { + CBORObject o = Read(ms, options); + CheckCBORLength((long)data.Length, (long)ms.Position); + return o; + } + } + + /// + public static CBORObject Divide(CBORObject first, CBORObject second) { + return CBORObjectMath.Divide(first, second); + } + + /// + public static CBORObject FromJSONString(string str) { + return FromJSONString(str, new CBOREncodeOptions(true, true)); + } + + /// + public static CBORObject FromJSONString( + string str, + CBOREncodeOptions options) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + if (str.Length > 0 && str[0] == 0xfeff) { + throw new CBORException( + "JSON object began with a byte order mark (U+FEFF) (offset 0)"); + } + var reader = new CharacterInputWithCount( + new CharacterReader(str, false, true)); + var nextchar = new int[1]; + CBORObject obj = CBORJson.ParseJSONValue( + reader, + !options.AllowDuplicateKeys, + false, + nextchar); + if (nextchar[0] != -1) { + reader.RaiseError("End of string not reached"); + } + return obj; + } + + /// + public object ToObject(Type t) { + return this.ToObject(t, null, null, 0); + } + + /// + public object ToObject(Type t, CBORTypeMapper mapper) { +if (mapper == null) { + throw new ArgumentNullException(nameof(mapper)); +} +return this.ToObject(t, mapper, null, 0); + } + + /// + public object ToObject(Type t, PODOptions options) { +if (options == null) { + throw new ArgumentNullException(nameof(options)); +} +return this.ToObject(t, null, options, 0); + } + + /// + public object ToObject(Type t, CBORTypeMapper mapper, PODOptions options) { +if (mapper == null) { + throw new ArgumentNullException(nameof(mapper)); +} +if (options == null) { + throw new ArgumentNullException(nameof(options)); +} +return this.ToObject(t, mapper, options, 0); + } + + internal object ToObject( + Type t, + CBORTypeMapper mapper, + PODOptions options, + int depth) { +depth++; +if (depth > 100) { + throw new CBORException("Depth level too high"); +} + if (t == null) { + throw new ArgumentNullException(nameof(t)); + } + if (t.Equals(typeof(CBORObject))) { + return this; + } + if (this.IsNull) { + return null; + } + if (mapper != null) { + object obj = mapper.ConvertBackWithConverter(this, t); + if (obj != null) { + return obj; + } + } + if (t.Equals(typeof(object))) { + return this; + } + return t.Equals(typeof(string)) ? this.AsString() : + PropertyMap2.TypeToObject(this, t, mapper, options, depth); + } + + /// + public static CBORObject FromObject(long value) { + return (value >= 0L && value < 24L) ? valueFixedObjects[(int)value] : + (new CBORObject(CBORObjectTypeInteger, value)); + } + + /// + public static CBORObject FromObject(CBORObject value) { + return value ?? CBORObject.Null; + } + + /// + [Obsolete("Use the EInteger version of this method.")] + public static CBORObject FromObject(BigInteger bigintValue) { + return ((object)bigintValue == (object)null) ? CBORObject.Null : + FromObject(PropertyMap.FromLegacy(bigintValue)); + } + + /// + public static CBORObject FromObject(EInteger bigintValue) { + if ((object)bigintValue == (object)null) { + return CBORObject.Null; + } + return (bigintValue.CompareTo(Int64MinValue) >= 0 && + bigintValue.CompareTo(Int64MaxValue) <= 0) ? + new CBORObject( + CBORObjectTypeInteger, + (long)(EInteger)bigintValue) : (new CBORObject( + CBORObjectTypeBigInteger, + bigintValue)); + } + + /// + [Obsolete("Use the EFloat version of this method instead.")] + public static CBORObject FromObject(ExtendedFloat bigValue) { + return ((object)bigValue == (object)null) ? CBORObject.Null : + FromObject(PropertyMap.FromLegacy(bigValue)); + } + + /// + public static CBORObject FromObject(EFloat bigValue) { + if ((object)bigValue == (object)null) { + return CBORObject.Null; + } + if (bigValue.IsInfinity()) { + return CBORObject.FromObject(bigValue.ToDouble()); + } + if (bigValue.IsNaN()) { + return new CBORObject( + CBORObjectTypeExtendedFloat, + bigValue); + } + EInteger bigintExponent = bigValue.Exponent; + return (bigintExponent.IsZero && !(bigValue.IsZero && + bigValue.IsNegative)) ? FromObject(bigValue.Mantissa) : + new CBORObject( + CBORObjectTypeExtendedFloat, + bigValue); + } + + /// + [Obsolete("Use the ERational version of this method instead.")] + public static CBORObject FromObject(ExtendedRational bigValue) { + return ((object)bigValue == (object)null) ? CBORObject.Null : + FromObject(PropertyMap.FromLegacy(bigValue)); + } + + /// + public static CBORObject FromObject(ERational bigValue) { + if ((object)bigValue == (object)null) { + return CBORObject.Null; + } + if (bigValue.IsInfinity()) { + return CBORObject.FromObject(bigValue.ToDouble()); + } + if (bigValue.IsNaN()) { + return new CBORObject( + CBORObjectTypeExtendedRational, + bigValue); + } + return (bigValue.IsFinite && bigValue.Denominator.Equals(EInteger.One)) ? + FromObject(bigValue.Numerator) : (new CBORObject( + CBORObjectTypeExtendedRational, + bigValue)); + } + + /// + public static CBORObject FromObject(EDecimal otherValue) { + if ((object)otherValue == (object)null) { + return CBORObject.Null; + } + if (otherValue.IsInfinity()) { + return CBORObject.FromObject(otherValue.ToDouble()); + } + if (otherValue.IsNaN()) { + return new CBORObject( + CBORObjectTypeExtendedDecimal, + otherValue); + } + EInteger bigintExponent = otherValue.Exponent; + return (bigintExponent.IsZero && !(otherValue.IsZero && + otherValue.IsNegative)) ? FromObject(otherValue.Mantissa) : + new CBORObject( + CBORObjectTypeExtendedDecimal, + otherValue); + } + + /// + [Obsolete("Use the EDecimal version of this method instead.")] + public static CBORObject FromObject(ExtendedDecimal otherValue) { + return ((object)otherValue == (object)null) ? CBORObject.Null : + FromObject(PropertyMap.FromLegacy(otherValue)); + } + + /// + public static CBORObject FromObject(string strValue) { + if (strValue == null) { + return CBORObject.Null; + } + if (DataUtilities.GetUtf8Length(strValue, false) < 0) { + throw new + ArgumentException("String contains an unpaired surrogate code point."); + } + return new CBORObject(CBORObjectTypeTextString, strValue); + } + + /// + public static CBORObject FromObject(int value) { + return (value >= 0 && value < 24) ? valueFixedObjects[value] : + FromObject((long)value); + } + + /// + public static CBORObject FromObject(short value) { + return (value >= 0 && value < 24) ? valueFixedObjects[value] : + FromObject((long)value); + } + + /// + public static CBORObject FromObject(char value) { + char[] valueChar = { value }; + return FromObject(new String(valueChar)); + } + + /// + public static CBORObject FromObject(bool value) { + return value ? CBORObject.True : CBORObject.False; + } + + /// + public static CBORObject FromObject(byte value) { + return FromObject(((int)value) & 0xff); + } + + /// + public static CBORObject FromObject(float value) { + return new CBORObject(CBORObjectTypeSingle, value); + } + + /// + public static CBORObject FromObject(double value) { + return new CBORObject(CBORObjectTypeDouble, value); + } + + /// + public static CBORObject FromObject(byte[] bytes) { + if (bytes == null) { + return CBORObject.Null; + } + var newvalue = new byte[bytes.Length]; + Array.Copy(bytes, 0, newvalue, 0, bytes.Length); + return new CBORObject(CBORObjectTypeByteString, bytes); + } + + /// + public static CBORObject FromObject(CBORObject[] array) { + if (array == null) { + return CBORObject.Null; + } + IList list = new List(); + foreach (CBORObject i in array) { + list.Add(FromObject(i)); + } + return new CBORObject(CBORObjectTypeArray, list); + } + + /// + public static CBORObject FromObject(int[] array) { + if (array == null) { + return CBORObject.Null; + } + IList list = new List(); + foreach (int i in array) { + list.Add(FromObject(i)); + } + return new CBORObject(CBORObjectTypeArray, list); + } + + /// + public static CBORObject FromObject(long[] array) { + if (array == null) { + return CBORObject.Null; + } + IList list = new List(); + foreach (long i in array) { + // Console.WriteLine(i); + list.Add(FromObject(i)); + } + return new CBORObject(CBORObjectTypeArray, list); + } + + /// + public static CBORObject FromObject(IList value) { + if (value == null) { + return CBORObject.Null; + } + CBORObject retCbor = CBORObject.NewArray(); + foreach (T i in (IList)value) { + retCbor.Add(CBORObject.FromObject(i)); + } + return retCbor; + } + + /// + public static CBORObject FromObject(IEnumerable value) { + if (value == null) { + return CBORObject.Null; + } + CBORObject retCbor = CBORObject.NewArray(); + foreach (T i in (IEnumerable)value) { + retCbor.Add(CBORObject.FromObject(i)); + } + return retCbor; + } + + /// + public static CBORObject FromObject(IDictionary dic) { + if (dic == null) { + return CBORObject.Null; + } + var map = new Dictionary(); + foreach (KeyValuePair entry in dic) { + CBORObject key = FromObject(entry.Key); + CBORObject value = FromObject(entry.Value); + map[key] = value; + } + return new CBORObject(CBORObjectTypeMap, map); + } + + /// + public static CBORObject FromObject(object obj) { + return FromObject(obj, PODOptions.Default); + } + + /// + public static CBORObject FromObject( + object obj, + PODOptions options) { + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + if (obj == null) { + return CBORObject.Null; + } + if (obj is string) { + return FromObject((string)obj); + } + if (obj is int) { + return FromObject((int)obj); + } + if (obj is long) { + return FromObject((long)obj); + } + if (obj is CBORObject) { + return FromObject((CBORObject)obj); + } + var eif = obj as EInteger; + if (eif != null) { + return FromObject(eif); + } + var edf = obj as EDecimal; + if (edf != null) { + return FromObject(edf); + } + var eff = obj as EFloat; + if (eff != null) { + return FromObject(eff); + } + var erf = obj as ERational; + if (erf != null) { + return FromObject(erf); + } +#pragma warning disable 618 + var bi = obj as BigInteger; + if (bi != null) { + return FromObject(bi); + } + var df = obj as ExtendedDecimal; + if (df != null) { + return FromObject(df); + } + var bf = obj as ExtendedFloat; + if (bf != null) { + return FromObject(bf); + } + var rf = obj as ExtendedRational; + if (rf != null) { + return FromObject(rf); + } +#pragma warning restore 618 + if (obj is short) { + return FromObject((short)obj); + } + if (obj is char) { + return FromObject((char)obj); + } + if (obj is bool) { + return FromObject((bool)obj); + } + if (obj is byte) { + return FromObject((byte)obj); + } + if (obj is float) { + return FromObject((float)obj); + } + if (obj is sbyte) { + return FromObject((sbyte)obj); + } + if (obj is ulong) { + return FromObject((ulong)obj); + } + if (obj is uint) { + return FromObject((uint)obj); + } + if (obj is ushort) { + return FromObject((ushort)obj); + } + if (obj is decimal) { + return FromObject((decimal)obj); + } + if (obj is Enum) { + return FromObject(PropertyMap.EnumToObject((Enum)obj)); + } + if (obj is double) { + return FromObject((double)obj); + } + byte[] bytearr = obj as byte[]; + if (bytearr != null) { + return FromObject(bytearr); + } + CBORObject objret; + if (obj is System.Collections.IDictionary) { + // IDictionary appears first because IDictionary includes IEnumerable + objret = CBORObject.NewMap(); + System.Collections.IDictionary objdic = + (System.Collections.IDictionary)obj; + foreach (object keyPair in (System.Collections.IDictionary)objdic) { + System.Collections.DictionaryEntry + kvp = (System.Collections.DictionaryEntry)keyPair; + CBORObject objKey = CBORObject.FromObject(kvp.Key, options); + objret[objKey] = CBORObject.FromObject(kvp.Value, options); + } + return objret; + } + if (obj is Array) { + return PropertyMap.FromArray(obj, options); + } + if (obj is System.Collections.IEnumerable) { + objret = CBORObject.NewArray(); + foreach (object element in (System.Collections.IEnumerable)obj) { + objret.Add(CBORObject.FromObject(element, options)); + } + return objret; + } + objret = ConvertWithConverter(obj); + if (objret != null) { + return objret; + } + objret = CBORObject.NewMap(); + #pragma warning disable 618 + foreach (KeyValuePair key in + PropertyMap.GetProperties( + obj, + options.RemoveIsPrefix, + options.UseCamelCase)) { + objret[key.Key] = CBORObject.FromObject(key.Value, options); + } + #pragma warning restore 618 + return objret; + } + + /// + [Obsolete("Use the EInteger version instead.")] + public static CBORObject FromObjectAndTag( + object valueOb, + BigInteger bigintTag) { + if (bigintTag == null) { + throw new ArgumentNullException(nameof(bigintTag)); + } + return FromObjectAndTag(valueOb, PropertyMap.FromLegacy(bigintTag)); + } + + /// + public static CBORObject FromObjectAndTag( + object valueOb, + EInteger bigintTag) { + if (bigintTag == null) { + throw new ArgumentNullException(nameof(bigintTag)); + } + if (bigintTag.Sign < 0) { + throw new ArgumentException("tagEInt's sign (" + bigintTag.Sign + + ") is less than 0"); + } + if (bigintTag.CompareTo(UInt64MaxValue) > 0) { + throw new ArgumentException( + "tag more than 18446744073709551615 (" + bigintTag + ")"); + } + CBORObject c = FromObject(valueOb); + if (bigintTag.GetSignedBitLength() <= 16) { + // Low-numbered, commonly used tags + return FromObjectAndTag(c, bigintTag.ToInt32Checked()); + } else { + var tagLow = 0; + var tagHigh = 0; + byte[] bytes = bigintTag.ToBytes(true); + for (var i = 0; i < Math.Min(4, bytes.Length); ++i) { + int b = ((int)bytes[i]) & 0xff; + tagLow = unchecked(tagLow | (((int)b) << (i * 8))); + } + for (int i = 4; i < Math.Min(8, bytes.Length); ++i) { + int b = ((int)bytes[i]) & 0xff; + tagHigh = unchecked(tagHigh | (((int)b) << (i * 8))); + } + var c2 = new CBORObject(c, tagLow, tagHigh); + #pragma warning disable 618 + ICBORTag tagconv = FindTagConverter(bigintTag); + #pragma warning restore 618 + if (tagconv != null) { + c2 = tagconv.ValidateObject(c2); + } + return c2; + } + } + + #pragma warning disable 612 + #pragma warning disable 618 + /// + public static CBORObject FromObjectAndTag( + object valueObValue, + int smallTag) { + if (smallTag < 0) { + throw new ArgumentException("smallTag (" + smallTag + + ") is less than 0"); + } + ICBORTag tagconv = FindTagConverter(smallTag); + CBORObject c = FromObject(valueObValue); + c = new CBORObject(c, smallTag, 0); + return (tagconv != null) ? tagconv.ValidateObject(c) : c; + } + #pragma warning restore 618 + #pragma warning restore 612 + + /// + public static CBORObject FromSimpleValue(int simpleValue) { + if (simpleValue < 0) { + throw new ArgumentException("simpleValue (" + simpleValue + + ") is less than 0"); + } + if (simpleValue > 255) { + throw new ArgumentException("simpleValue (" + simpleValue + + ") is more than " + "255"); + } + if (simpleValue >= 24 && simpleValue < 32) { + throw new ArgumentException("Simple value is from 24 to 31: " + + simpleValue); + } + if (simpleValue < 32) { + return valueFixedObjects[0xe0 + simpleValue]; + } + return new CBORObject( + CBORObjectTypeSimpleValue, + simpleValue); + } + + /// + public static CBORObject Multiply(CBORObject first, CBORObject second) { + return CBORObjectMath.Multiply(first, second); + } + + /// + public static CBORObject NewArray() { + return new CBORObject(CBORObjectTypeArray, new List()); + } + + /// + public static CBORObject NewMap() { + return FromObject(new Dictionary()); + } + + /// + public static CBORObject Read(Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + try { + var reader = new CBORReader(stream); + return reader.ResolveSharedRefsIfNeeded(reader.Read(null)); + } catch (IOException ex) { + throw new CBORException("I/O error occurred.", ex); + } + } + + /// + public static CBORObject Read(Stream stream, CBOREncodeOptions options) { + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + try { + var reader = new CBORReader(stream); + if (!options.AllowDuplicateKeys) { + reader.DuplicatePolicy = CBORReader.CBORDuplicatePolicy.Disallow; + } + return reader.ResolveSharedRefsIfNeeded(reader.Read(null)); + } catch (IOException ex) { + throw new CBORException("I/O error occurred.", ex); + } + } + + /// + public static CBORObject ReadJSON(Stream stream) { + return ReadJSON(stream, new CBOREncodeOptions(true, true)); + } + + /// + public static CBORObject ReadJSON( + Stream stream, + CBOREncodeOptions options) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + var reader = new CharacterInputWithCount( + new CharacterReader(stream, 2, true)); + try { + var nextchar = new int[1]; + CBORObject obj = CBORJson.ParseJSONValue( + reader, + !options.AllowDuplicateKeys, + false, + nextchar); + if (nextchar[0] != -1) { + reader.RaiseError("End of data stream not reached"); + } + return obj; + } catch (CBORException ex) { + var ioex = ex.InnerException as IOException; + if (ioex != null) { + throw ioex; + } + throw; + } + } + + /// + public static CBORObject Remainder(CBORObject first, CBORObject second) { + return CBORObjectMath.Remainder(first, second); + } + + /// + public static CBORObject Subtract(CBORObject first, CBORObject second) { + return CBORObjectMath.Subtract(first, second); + } + + /// + public static void Write(string str, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + // TODO: Use CBOREncodeOptions.Default in future versions + Write(str, stream, new CBOREncodeOptions(true, true)); + } + + /// + public static void Write( + string str, + Stream stream, + CBOREncodeOptions options) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + if (str == null) { + stream.WriteByte(0xf6); // Write null instead of string + } else { + if (!options.UseIndefLengthStrings || options.Ctap2Canonical) { + // NOTE: Length of a String object won't be higher than the maximum + // allowed for definite-length strings + long codePointLength = DataUtilities.GetUtf8Length(str, true); + WritePositiveInt64(3, codePointLength, stream); + DataUtilities.WriteUtf8(str, stream, true); + } else { + WriteStreamedString(str, stream); + } + } + } + + /// + [Obsolete("Pass an EFloat to the Write method instead.")] + public static void Write(ExtendedFloat bignum, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (bignum == null) { + stream.WriteByte(0xf6); + return; + } + Write(PropertyMap.FromLegacy(bignum), stream); + } + + /// + public static void Write(EFloat bignum, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (bignum == null) { + stream.WriteByte(0xf6); + return; + } + if ((bignum.IsZero && bignum.IsNegative) || bignum.IsInfinity() || + bignum.IsNaN()) { + Write(bignum.ToDouble(), stream); + return; + } + EInteger exponent = bignum.Exponent; + if (exponent.IsZero) { + Write(bignum.Mantissa, stream); + } else { + if (!BigIntFits(exponent)) { + stream.WriteByte(0xd9); // tag 265 + stream.WriteByte(0x01); + stream.WriteByte(0x09); + stream.WriteByte(0x82); // array, length 2 + } else { + stream.WriteByte(0xc5); // tag 5 + stream.WriteByte(0x82); // array, length 2 + } + Write(bignum.Exponent, stream); + Write(bignum.Mantissa, stream); + } + } + + /// + [Obsolete("Pass an ERational to the Write method instead.")] + public static void Write(ExtendedRational rational, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (rational == null) { + stream.WriteByte(0xf6); + return; + } + Write(PropertyMap.FromLegacy(rational), stream); + } + + /// + public static void Write(ERational rational, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (rational == null) { + stream.WriteByte(0xf6); + return; + } + if (!rational.IsFinite || (rational.IsNegative && rational.IsZero)) { + Write(rational.ToDouble(), stream); + return; + } + if (rational.Denominator.Equals(EInteger.One)) { + Write(rational.Numerator, stream); + return; + } + stream.WriteByte(0xd8); // tag 30 + stream.WriteByte(0x1e); + stream.WriteByte(0x82); // array, length 2 + Write(rational.Numerator, stream); + Write(rational.Denominator, stream); + } + + /// + [Obsolete("Pass an EDecimal to the Write method instead.")] + public static void Write(ExtendedDecimal bignum, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (bignum == null) { + stream.WriteByte(0xf6); + return; + } + Write(PropertyMap.FromLegacy(bignum), stream); + } + + /// + public static void Write(EDecimal bignum, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (bignum == null) { + stream.WriteByte(0xf6); + return; + } + if ((bignum.IsZero && bignum.IsNegative) || bignum.IsInfinity() || + bignum.IsNaN()) { + Write(bignum.ToDouble(), stream); + return; + } + EInteger exponent = bignum.Exponent; + if (exponent.IsZero) { + Write(bignum.Mantissa, stream); + } else { + if (!BigIntFits(exponent)) { + stream.WriteByte(0xd9); // tag 264 + stream.WriteByte(0x01); + stream.WriteByte(0x08); + stream.WriteByte(0x82); // array, length 2 + } else { + stream.WriteByte(0xc4); // tag 4 + stream.WriteByte(0x82); // array, length 2 + } + Write(bignum.Exponent, stream); + Write(bignum.Mantissa, stream); + } + } + + /// + [Obsolete("Pass an EInteger to the Write method instead.")] + public static void Write(BigInteger bigint, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if ((object)bigint == (object)null) { + stream.WriteByte(0xf6); + return; + } + Write(PropertyMap.FromLegacy(bigint), stream); + } + + /// + public static void Write(EInteger bigint, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if ((object)bigint == (object)null) { + stream.WriteByte(0xf6); + return; + } + var datatype = 0; + if (bigint.Sign < 0) { + datatype = 1; + bigint = bigint.Add(EInteger.One); + bigint = -(EInteger)bigint; + } + if (bigint.CompareTo(Int64MaxValue) <= 0) { + // If the big integer is representable as a long and in + // major type 0 or 1, write that major type + // instead of as a bignum + var ui = (long)(EInteger)bigint; + WritePositiveInt64(datatype, ui, stream); + } else { + // Get a byte array of the big integer's value, + // since shifting and doing AND operations is + // slow with large BigIntegers + byte[] bytes = bigint.ToBytes(true); + int byteCount = bytes.Length; + while (byteCount > 0 && bytes[byteCount - 1] == 0) { + // Ignore trailing zero bytes + --byteCount; + } + if (byteCount != 0) { + int half = byteCount >> 1; + int right = byteCount - 1; + for (var i = 0; i < half; ++i, --right) { + byte value = bytes[i]; + bytes[i] = bytes[right]; + bytes[right] = value; + } + } + switch (byteCount) { + case 0: + stream.WriteByte((byte)(datatype << 5)); + return; + case 1: + WritePositiveInt(datatype, ((int)bytes[0]) & 0xff, stream); + break; + case 2: + stream.WriteByte((byte)((datatype << 5) | 25)); + stream.Write(bytes, 0, byteCount); + break; + case 3: + stream.WriteByte((byte)((datatype << 5) | 26)); + stream.WriteByte((byte)0); + stream.Write(bytes, 0, byteCount); + break; + case 4: + stream.WriteByte((byte)((datatype << 5) | 26)); + stream.Write(bytes, 0, byteCount); + break; + case 5: + stream.WriteByte((byte)((datatype << 5) | 27)); + stream.WriteByte((byte)0); + stream.WriteByte((byte)0); + stream.WriteByte((byte)0); + stream.Write(bytes, 0, byteCount); + break; + case 6: + stream.WriteByte((byte)((datatype << 5) | 27)); + stream.WriteByte((byte)0); + stream.WriteByte((byte)0); + stream.Write(bytes, 0, byteCount); + break; + case 7: + stream.WriteByte((byte)((datatype << 5) | 27)); + stream.WriteByte((byte)0); + stream.Write(bytes, 0, byteCount); + break; + case 8: + stream.WriteByte((byte)((datatype << 5) | 27)); + stream.Write(bytes, 0, byteCount); + break; + default: stream.WriteByte((datatype == 0) ? +(byte)0xc2 : (byte)0xc3); + WritePositiveInt(2, byteCount, stream); + stream.Write(bytes, 0, byteCount); + break; + } + } + } + + /// + public static void Write(long value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (value >= 0) { + WritePositiveInt64(0, value, stream); + } else { + ++value; + value = -value; // Will never overflow + WritePositiveInt64(1, value, stream); + } + } + + /// + public static void Write(int value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + var type = 0; + if (value < 0) { + ++value; + value = -value; + type = 0x20; + } + if (value < 24) { + stream.WriteByte((byte)(value | type)); + } else if (value <= 0xff) { + byte[] bytes = { (byte)(24 | type), (byte)(value & 0xff) }; + stream.Write(bytes, 0, 2); + } else if (value <= 0xffff) { + byte[] bytes = { (byte)(25 | type), (byte)((value >> 8) & 0xff), + (byte)(value & 0xff) }; + stream.Write(bytes, 0, 3); + } else { + byte[] bytes = { (byte)(26 | type), (byte)((value >> 24) & 0xff), + (byte)((value >> 16) & 0xff), (byte)((value >> 8) & 0xff), + (byte)(value & 0xff) }; + stream.Write(bytes, 0, 5); + } + } + + /// + public static void Write(short value, Stream stream) { + Write((long)value, stream); + } + + /// + public static void Write(char value, Stream stream) { + if (value >= 0xd800 && value < 0xe000) { + throw new ArgumentException("Value is a surrogate code point."); + } + char[] valueChar = { value }; + Write(new String(valueChar), stream); + } + + /// + public static void Write(bool value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + stream.WriteByte(value ? (byte)0xf5 : (byte)0xf4); + } + + /// + public static void Write(byte value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if ((((int)value) & 0xff) < 24) { + stream.WriteByte(value); + } else { + stream.WriteByte((byte)24); + stream.WriteByte(value); + } + } + + /// + public static void Write(float value, Stream s) { + if (s == null) { + throw new ArgumentNullException(nameof(s)); + } + int bits = BitConverter.ToInt32(BitConverter.GetBytes((float)value), 0); + byte[] data = { (byte)0xfa, (byte)((bits >> 24) & 0xff), + (byte)((bits >> 16) & 0xff), (byte)((bits >> 8) & 0xff), + (byte)(bits & 0xff) }; + s.Write(data, 0, 5); + } + + /// + public static void Write(double value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + long bits = + BitConverter.ToInt64( + BitConverter.GetBytes((double)value), + 0); + byte[] data = { (byte)0xfb, + (byte)((bits >> 56) & 0xff), (byte)((bits >> 48) & 0xff), + (byte)((bits >> 40) & 0xff), (byte)((bits >> 32) & 0xff), + (byte)((bits >> 24) & 0xff), (byte)((bits >> 16) & 0xff), + (byte)((bits >> 8) & 0xff), (byte)(bits & 0xff) }; + stream.Write(data, 0, 9); + } + + /// + public static void Write(CBORObject value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (value == null) { + stream.WriteByte(0xf6); + } else { + value.WriteTo(stream); + } + } + + /// + public static void Write(object objValue, Stream stream) { + // TODO: Use CBOREncodeOptions.Default in future versions + Write(objValue, stream, new CBOREncodeOptions(true, true)); + } + + /// + public static void Write( + object objValue, + Stream output, + CBOREncodeOptions options) { + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + if (output == null) { + throw new ArgumentNullException(nameof(output)); + } + if (objValue == null) { + output.WriteByte(0xf6); + return; + } + if (options.Ctap2Canonical) { + FromObject(objValue).WriteTo(output, options); + return; + } + byte[] data = objValue as byte[]; + if (data != null) { + WritePositiveInt(3, data.Length, output); + output.Write(data, 0, data.Length); + return; + } + if (objValue is IList) { + WriteObjectArray( + (IList)objValue, + output, + options); + return; + } + if (objValue is IDictionary) { + WriteObjectMap( + (IDictionary)objValue, + output, + options); + return; + } + FromObject(objValue).WriteTo(output, options); + } + + /// + public static void WriteJSON(object obj, Stream outputStream) { + if (obj == null) { + outputStream.Write(ValueNullBytes, 0, ValueNullBytes.Length); + return; + } + if (obj is bool) { + if ((bool)obj) { + outputStream.Write(ValueTrueBytes, 0, ValueTrueBytes.Length); + return; + } + outputStream.Write(ValueFalseBytes, 0, ValueFalseBytes.Length); + return; + } + CBORObject.FromObject(obj).WriteJSONTo(outputStream); + } + + /// + public CBORObject Abs() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("This object is not a number."); + } + object oldItem = this.ThisItem; + object newItem = cn.Abs(oldItem); + if (oldItem == newItem) { + return this; + } + if (newItem is EDecimal) { + return CBORObject.FromObject((EDecimal)newItem); + } + if (newItem is EInteger) { + return CBORObject.FromObject((EInteger)newItem); + } + if (newItem is EFloat) { + return CBORObject.FromObject((EFloat)newItem); + } + var rat = newItem as ERational; + return (rat != null) ? CBORObject.FromObject(rat) : ((oldItem == + newItem) ? this : CBORObject.FromObject(newItem)); + } + + /// + public CBORObject Add(object key, object valueOb) { + if (this.ItemType == CBORObjectTypeMap) { + CBORObject mapKey; + CBORObject mapValue; + if (key == null) { + mapKey = CBORObject.Null; + } else { + mapKey = key as CBORObject; + mapKey = mapKey ?? CBORObject.FromObject(key); + } + if (valueOb == null) { + mapValue = CBORObject.Null; + } else { + mapValue = valueOb as CBORObject; + mapValue = mapValue ?? CBORObject.FromObject(valueOb); + } + IDictionary map = this.AsMap(); + if (map.ContainsKey(mapKey)) { + throw new ArgumentException("Key already exists"); + } + map.Add(mapKey, mapValue); + } else { + throw new InvalidOperationException("Not a map"); + } + return this; + } + + /// + public CBORObject Add(CBORObject obj) { + if (this.ItemType == CBORObjectTypeArray) { + IList list = this.AsList(); + list.Add(obj); + return this; + } + throw new InvalidOperationException("Not an array"); + } + + /// + public CBORObject Add(object obj) { + if (this.ItemType == CBORObjectTypeArray) { + IList list = this.AsList(); + list.Add(CBORObject.FromObject(obj)); + return this; + } + throw new InvalidOperationException("Not an array"); + } + + /// + [Obsolete("Use the AsEInteger method instead.")] + public BigInteger AsBigInteger() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return BigInteger.fromBytes( + cn.AsEInteger(this.ThisItem).ToBytes(true), + true); + } + + /// + public EInteger AsEInteger() { + // TODO: Consider returning null if this object is null + // in next major version + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsEInteger(this.ThisItem); + } + + /// + public bool AsBoolean() { + return !this.IsFalse && !this.IsNull && !this.IsUndefined; + } + + /// + public byte AsByte() { + return (byte)this.AsInt32(0, 255); + } + + /// + public double AsDouble() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsDouble(this.ThisItem); + } + + /// + [Obsolete("Use AsEDecimal instead.")] + public ExtendedDecimal AsExtendedDecimal() { + return ExtendedDecimal.FromString(this.AsEDecimal().ToString()); + } + + /// + public EDecimal AsEDecimal() { + // TODO: Consider returning null if this object is null + // in next major version + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsExtendedDecimal(this.ThisItem); + } + + /// + [Obsolete("Use AsEFloat instead.")] + public ExtendedFloat AsExtendedFloat() { + return ExtendedFloat.FromString(this.AsEFloat().ToString()); + } + + /// + public EFloat AsEFloat() { + // TODO: Consider returning null if this object is null + // in next major version + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsExtendedFloat(this.ThisItem); + } + + /// + [Obsolete("Use AsERational instead.")] + public ExtendedRational AsExtendedRational() { + return PropertyMap.ToLegacy(this.AsERational()); + } + + /// + // TODO: Consider returning null if this object is null + // in next major version + public ERational AsERational() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsExtendedRational(this.ThisItem); + } + + /// + public short AsInt16() { + return (short)this.AsInt32(Int16.MinValue, Int16.MaxValue); + } + + /// + public int AsInt32() { + return this.AsInt32(Int32.MinValue, Int32.MaxValue); + } + + /// + public long AsInt64() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsInt64(this.ThisItem); + } + + /// + public float AsSingle() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + return cn.AsSingle(this.ThisItem); + } + + /// + public string AsString() { + // TODO: Consider returning null if this object is null + // in next major version + int type = this.ItemType; + switch (type) { + case CBORObjectTypeTextString: { + return (string)this.ThisItem; + } + default: throw new InvalidOperationException("Not a string type"); + } + } + + /// + public bool CanFitInDouble() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return (cn != null) && cn.CanFitInDouble(this.ThisItem); + } + + /// + public bool CanFitInInt32() { + if (!this.CanFitInInt64()) { + return false; + } + long v = this.AsInt64(); + return v >= Int32.MinValue && v <= Int32.MaxValue; + } + + /// + public bool CanFitInInt64() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return (cn != null) && cn.CanFitInInt64(this.ThisItem); + } + + /// + public bool CanFitInSingle() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return (cn != null) && cn.CanFitInSingle(this.ThisItem); + } + + /// + public bool CanTruncatedIntFitInInt32() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return (cn != null) && cn.CanTruncatedIntFitInInt32(this.ThisItem); + } + + /// + public bool CanTruncatedIntFitInInt64() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return cn != null && cn.CanTruncatedIntFitInInt64(this.ThisItem); + } + + /// + public int CompareTo(CBORObject other) { + if (other == null) { + return 1; + } + if (this == other) { + return 0; + } + int typeA = this.ItemType; + int typeB = other.ItemType; + object objA = this.ThisItem; + object objB = other.ThisItem; + var simpleValueA = -1; + var simpleValueB = -1; + if (typeA == CBORObjectTypeSimpleValue) { + if ((int)objA == 20) { // false + simpleValueA = 2; + } else if ((int)objA == 21) { // true + simpleValueA = 3; + } else if ((int)objA == 22) { // null + simpleValueA = 1; + } else if ((int)objA == 23) { // undefined + simpleValueA = 0; + } + } + if (typeB == CBORObjectTypeSimpleValue) { + if ((int)objB == 20) { // false + simpleValueB = 2; + } else if ((int)objB == 21) { // true + simpleValueB = 3; + } else if ((int)objB == 22) { // null + simpleValueB = 1; + } else if ((int)objB == 23) { // undefined + simpleValueB = 0; + } + } + var cmp = 0; + if (simpleValueA >= 0 || simpleValueB >= 0) { + if (simpleValueB < 0) { + return -1; // B is not true, false, null, or undefined, so A is less + } + if (simpleValueA < 0) { + return 1; + } + cmp = (simpleValueA == simpleValueB) ? 0 : ((simpleValueA < + simpleValueB) ? -1 : 1); + } else if (typeA == typeB) { + switch (typeA) { + case CBORObjectTypeInteger: { + var a = (long)objA; + var b = (long)objB; + cmp = (a == b) ? 0 : ((a < b) ? -1 : 1); + break; + } + case CBORObjectTypeSingle: { + var a = (float)objA; + var b = (float)objB; + // Treat NaN as greater than all other numbers + cmp = Single.IsNaN(a) ? (Single.IsNaN(b) ? 0 : 1) : + (Single.IsNaN(b) ? (-1) : ((a == b) ? 0 : ((a < b) ? -1 : + 1))); + break; + } + case CBORObjectTypeBigInteger: { + var bigintA = (EInteger)objA; + var bigintB = (EInteger)objB; + cmp = bigintA.CompareTo(bigintB); + break; + } + case CBORObjectTypeDouble: { + var a = (double)objA; + var b = (double)objB; + // Treat NaN as greater than all other numbers + cmp = Double.IsNaN(a) ? (Double.IsNaN(b) ? 0 : 1) : + (Double.IsNaN(b) ? (-1) : ((a == b) ? 0 : ((a < b) ? -1 : + 1))); + break; + } + case CBORObjectTypeExtendedDecimal: { + cmp = ((EDecimal)objA).CompareTo((EDecimal)objB); + break; + } + case CBORObjectTypeExtendedFloat: { + cmp = ((EFloat)objA).CompareTo( + (EFloat)objB); + break; + } + case CBORObjectTypeExtendedRational: { + cmp = ((ERational)objA).CompareTo( + (ERational)objB); + break; + } + case CBORObjectTypeByteString: { + cmp = CBORUtilities.ByteArrayCompare((byte[])objA, (byte[])objB); + break; + } + case CBORObjectTypeTextString: { + cmp = DataUtilities.CodePointCompare( + (string)objA, + (string)objB); + break; + } + case CBORObjectTypeArray: { + cmp = ListCompare( + (List)objA, + (List)objB); + break; + } + case CBORObjectTypeMap: { + cmp = MapCompare( + (IDictionary)objA, + (IDictionary)objB); + break; + } + case CBORObjectTypeSimpleValue: { + var valueA = (int)objA; + var valueB = (int)objB; + cmp = (valueA == valueB) ? 0 : ((valueA < valueB) ? -1 : 1); + break; + } + default: throw new ArgumentException("Unexpected data type"); + } + } else { + int typeOrderA = ValueNumberTypeOrder[typeA]; + int typeOrderB = ValueNumberTypeOrder[typeB]; + // Check whether general types are different + // (treating number types the same) + if (typeOrderA != typeOrderB) { + return (typeOrderA < typeOrderB) ? -1 : 1; + } + // At this point, both types should be number types. +#if DEBUG + if (typeOrderA != 0) { + throw new ArgumentException("doesn't satisfy typeOrderA == 0"); + } + if (typeOrderB != 0) { + throw new ArgumentException("doesn't satisfy typeOrderB == 0"); + } +#endif + int s1 = GetSignInternal(typeA, objA); + int s2 = GetSignInternal(typeB, objB); + if (s1 != s2 && s1 != 2 && s2 != 2) { + // if both types are numbers + // and their signs are different + return (s1 < s2) ? -1 : 1; + } + if (s1 == 2 && s2 == 2) { + // both are NaN + cmp = 0; + } else if (s1 == 2) { + // first object is NaN + return 1; + } else if (s2 == 2) { + // second object is NaN + return -1; + } else { + // DebugUtility.Log("a=" + this + " b=" + other); + if (typeA == CBORObjectTypeExtendedRational) { + ERational e1 = NumberInterfaces[typeA].AsExtendedRational(objA); + if (typeB == CBORObjectTypeExtendedDecimal) { + EDecimal e2 = NumberInterfaces[typeB].AsExtendedDecimal(objB); + cmp = e1.CompareToDecimal(e2); + } else { + EFloat e2 = NumberInterfaces[typeB].AsExtendedFloat(objB); + cmp = e1.CompareToBinary(e2); + } + } else if (typeB == CBORObjectTypeExtendedRational) { + ERational e2 = NumberInterfaces[typeB].AsExtendedRational(objB); + if (typeA == CBORObjectTypeExtendedDecimal) { + EDecimal e1 = NumberInterfaces[typeA].AsExtendedDecimal(objA); + cmp = e2.CompareToDecimal(e1); + cmp = -cmp; + } else { + EFloat e1 = NumberInterfaces[typeA].AsExtendedFloat(objA); + cmp = e2.CompareToBinary(e1); + cmp = -cmp; + } + } else if (typeA == CBORObjectTypeExtendedDecimal || + typeB == CBORObjectTypeExtendedDecimal) { + EDecimal e1 = null; + EDecimal e2 = null; + if (typeA == CBORObjectTypeExtendedFloat) { + var ef1 = (EFloat)objA; + e2 = (EDecimal)objB; + cmp = e2.CompareToBinary(ef1); + cmp = -cmp; + } else if (typeB == CBORObjectTypeExtendedFloat) { + var ef1 = (EFloat)objB; + e2 = (EDecimal)objA; + cmp = e2.CompareToBinary(ef1); + } else { + e1 = NumberInterfaces[typeA].AsExtendedDecimal(objA); + e2 = NumberInterfaces[typeB].AsExtendedDecimal(objB); + cmp = e1.CompareTo(e2); + } + } else if (typeA == CBORObjectTypeExtendedFloat || typeB == + CBORObjectTypeExtendedFloat || + typeA == CBORObjectTypeDouble || typeB == + CBORObjectTypeDouble || + typeA == CBORObjectTypeSingle || typeB == + CBORObjectTypeSingle) { + EFloat e1 = NumberInterfaces[typeA].AsExtendedFloat(objA); + EFloat e2 = NumberInterfaces[typeB].AsExtendedFloat(objB); + cmp = e1.CompareTo(e2); + } else { + EInteger b1 = NumberInterfaces[typeA].AsEInteger(objA); + EInteger b2 = NumberInterfaces[typeB].AsEInteger(objB); + cmp = b1.CompareTo(b2); + } + } + } + return (cmp == 0) ? ((!this.IsTagged && !other.IsTagged) ? 0 : + TagsCompare(this.GetAllTags(), other.GetAllTags())) : + cmp; + } + + /// + public int CompareToIgnoreTags(CBORObject other) { + return (other == null) ? 1 : ((this == other) ? 0 : + this.Untag().CompareTo(other.Untag())); + } + + /// + public bool ContainsKey(CBORObject key) { + if (key == null) { + throw new ArgumentNullException(nameof(key)); + } + if (this.ItemType == CBORObjectTypeMap) { + IDictionary map = this.AsMap(); + return map.ContainsKey(key); + } + return false; + } + + /// + public bool ContainsKey(string key) { + if (key == null) { + throw new ArgumentNullException(nameof(key)); + } + if (this.ItemType == CBORObjectTypeMap) { + IDictionary map = this.AsMap(); + return map.ContainsKey(CBORObject.FromObject(key)); + } + return false; + } + + /// + public byte[] EncodeToBytes() { + return this.EncodeToBytes(new CBOREncodeOptions(true, true)); + } + + /// + public byte[] EncodeToBytes(CBOREncodeOptions options) { + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + if (options.Ctap2Canonical) { + return CBORCanonical.CtapCanonicalEncode(this); + } + // For some types, a memory stream is a lot of + // overhead since the amount of memory the types + // use is fixed and small + var hasComplexTag = false; + byte tagbyte = 0; + bool tagged = this.IsTagged; + if (this.IsTagged) { + var taggedItem = (CBORObject)this.itemValue; + if (taggedItem.IsTagged || this.tagHigh != 0 || + (this.tagLow >> 16) != 0 || this.tagLow >= 24) { + hasComplexTag = true; + } else { + tagbyte = (byte)(0xc0 + (int)this.tagLow); + } + } + if (!hasComplexTag) { + switch (this.ItemType) { + case CBORObjectTypeTextString: { + byte[] ret = GetOptimizedBytesIfShortAscii( + this.AsString(), tagged ? (((int)tagbyte) & 0xff) : -1); + if (ret != null) { + return ret; + } + break; + } + case CBORObjectTypeSimpleValue: { + if (tagged) { + if (this.IsFalse) { + return new[] { tagbyte, (byte)0xf4 }; + } + if (this.IsTrue) { + return new[] { tagbyte, (byte)0xf5 }; + } + if (this.IsNull) { + return new[] { tagbyte, (byte)0xf6 }; + } + if (this.IsUndefined) { + return new[] { tagbyte, (byte)0xf7 }; + } + } else { + if (this.IsFalse) { + return new[] { (byte)0xf4 }; + } + if (this.IsTrue) { + return new[] { (byte)0xf5 }; + } + if (this.IsNull) { + return new[] { (byte)0xf6 }; + } + if (this.IsUndefined) { + return new[] { (byte)0xf7 }; + } + } + break; + } + case CBORObjectTypeInteger: { + var value = (long)this.ThisItem; + byte[] intBytes = null; + if (value >= 0) { + intBytes = GetPositiveInt64Bytes(0, value); + } else { + ++value; + value = -value; // Will never overflow + intBytes = GetPositiveInt64Bytes(1, value); + } + if (!tagged) { + return intBytes; + } + var ret2 = new byte[intBytes.Length + 1]; + Array.Copy(intBytes, 0, ret2, 1, intBytes.Length); + ret2[0] = tagbyte; + return ret2; + } + case CBORObjectTypeSingle: { + var value = (float)this.ThisItem; + int bits = BitConverter.ToInt32(BitConverter.GetBytes(value), 0); + return tagged ? new[] { tagbyte, (byte)0xfa, + (byte)((bits >> 24) & 0xff), (byte)((bits >> 16) & 0xff), + (byte)((bits >> 8) & 0xff), (byte)(bits & 0xff) } : + new[] { (byte)0xfa, (byte)((bits >> 24) & 0xff), + (byte)((bits >> 16) & 0xff), (byte)((bits >> 8) & 0xff), + (byte)(bits & 0xff) }; + } + case CBORObjectTypeDouble: { + var value = (double)this.ThisItem; + long bits = BitConverter.ToInt64(BitConverter.GetBytes(value), 0); + return tagged ? new[] { tagbyte, (byte)0xfb, + (byte)((bits >> 56) & 0xff), (byte)((bits >> 48) & 0xff), + (byte)((bits >> 40) & 0xff), (byte)((bits >> 32) & 0xff), + (byte)((bits >> 24) & 0xff), (byte)((bits >> 16) & 0xff), + (byte)((bits >> 8) & 0xff), (byte)(bits & 0xff) } : + new[] { (byte)0xfb, (byte)((bits >> 56) & 0xff), + (byte)((bits >> 48) & 0xff), (byte)((bits >> 40) & 0xff), + (byte)((bits >> 32) & 0xff), (byte)((bits >> 24) & 0xff), + (byte)((bits >> 16) & 0xff), (byte)((bits >> 8) & 0xff), + (byte)(bits & 0xff) }; + } + } + } + try { + using (var ms = new MemoryStream(16)) { + this.WriteTo(ms, options); + return ms.ToArray(); + } + } catch (IOException ex) { + throw new CBORException("I/O Error occurred", ex); + } + } + + /// + public override bool Equals(object obj) { + return this.Equals(obj as CBORObject); + } + + /// + public bool Equals(CBORObject other) { + var otherValue = other as CBORObject; + if (otherValue == null) { + return false; + } + if (this == otherValue) { + return true; + } + switch (this.itemtypeValue) { + case CBORObjectTypeByteString: + if (!CBORUtilities.ByteArrayEquals( + (byte[])this.ThisItem, + otherValue.itemValue as byte[])) { + return false; + } + break; + case CBORObjectTypeMap: { + IDictionary cbordict = + otherValue.itemValue as IDictionary; + if (!CBORMapEquals(this.AsMap(), cbordict)) { + return false; + } + break; + } + case CBORObjectTypeArray: + if (!CBORArrayEquals( + this.AsList(), + otherValue.itemValue as IList)) { + return false; + } + break; + default: + if (!Object.Equals(this.itemValue, otherValue.itemValue)) { + return false; + } + break; + } + return this.itemtypeValue == otherValue.itemtypeValue && + this.tagLow == otherValue.tagLow && this.tagHigh == otherValue.tagHigh; + } + + /// + public byte[] GetByteString() { + if (this.ItemType == CBORObjectTypeByteString) { + return (byte[])this.ThisItem; + } + throw new InvalidOperationException("Not a byte string"); + } + + /// + public override int GetHashCode() { + var hashCode = 651869431; + unchecked { + if (this.itemValue != null) { + var itemHashCode = 0; + long longValue = 0L; + switch (this.itemtypeValue) { + case CBORObjectTypeByteString: + itemHashCode = + CBORUtilities.ByteArrayHashCode((byte[])this.ThisItem); + break; + case CBORObjectTypeMap: + itemHashCode = CBORMapHashCode(this.AsMap()); + break; + case CBORObjectTypeArray: + itemHashCode = CBORArrayHashCode(this.AsList()); + break; + case CBORObjectTypeTextString: + itemHashCode = StringHashCode((string)this.itemValue); + break; + case CBORObjectTypeSimpleValue: + itemHashCode = (int)this.itemValue; + break; + case CBORObjectTypeSingle: + itemHashCode = + BitConverter.ToInt32(BitConverter.GetBytes((float)this.itemValue), 0); + break; + case CBORObjectTypeDouble: + longValue = + BitConverter.ToInt64(BitConverter.GetBytes((double)this.itemValue), 0); + longValue |= longValue >> 32; + itemHashCode = unchecked((int)longValue); + break; + case CBORObjectTypeInteger: + longValue = (long)this.itemValue; + longValue |= longValue >> 32; + itemHashCode = unchecked((int)longValue); + break; + default: + // EInteger, EFloat, EDecimal, ERational, CBORObject + itemHashCode = this.itemValue.GetHashCode(); + break; + } + hashCode += 651869479 * itemHashCode; + } + hashCode += 651869483 * (this.itemtypeValue + + this.tagLow + this.tagHigh); + } + return hashCode; + } + + /// + [Obsolete("Use the GetAllTags method instead.")] + public BigInteger[] GetTags() { + EInteger[] etags = this.GetAllTags(); + if (etags.Length == 0) { + return new BigInteger[0]; + } + var bigret = new BigInteger[etags.Length]; + for (var i = 0; i < bigret.Length; ++i) { + bigret[i] = PropertyMap.ToLegacy(etags[i]); + } + return bigret; + } + + /// + public EInteger[] GetAllTags() { + if (!this.IsTagged) { + return ValueEmptyTags; + } + CBORObject curitem = this; + if (curitem.IsTagged) { + var list = new List(); + while (curitem.IsTagged) { + list.Add( + LowHighToEInteger( + curitem.tagLow, + curitem.tagHigh)); + curitem = (CBORObject)curitem.itemValue; + } + return (EInteger[])list.ToArray(); + } + return new[] { LowHighToEInteger(this.tagLow, this.tagHigh) }; + } + + /// + public bool HasMostOuterTag(int tagValue) { + if (tagValue < 0) { + throw new ArgumentException("tagValue (" + tagValue + + ") is less than 0"); + } + return this.IsTagged && this.tagHigh == 0 && this.tagLow == tagValue; + } + + /// + public bool HasMostOuterTag(EInteger bigTagValue) { + if (bigTagValue == null) { + throw new ArgumentNullException(nameof(bigTagValue)); +} + if (bigTagValue.Sign < 0) { + throw new ArgumentException("bigTagValue (" + bigTagValue + + ") is less than 0"); + } + return (!this.IsTagged) ? false : this.MostOuterTag.Equals(bigTagValue); + } + + /// + public bool HasTag(int tagValue) { + if (tagValue < 0) { + throw new ArgumentException("tagValue (" + tagValue + + ") is less than 0"); + } + CBORObject obj = this; + while (true) { + if (!obj.IsTagged) { + return false; + } + if (obj.tagHigh == 0 && tagValue == obj.tagLow) { + return true; + } + obj = (CBORObject)obj.itemValue; +#if DEBUG + if (obj == null) { + throw new ArgumentNullException(nameof(tagValue)); + } +#endif + } + } + + /// + [Obsolete("Use the EInteger version of this method.")] + public bool HasTag(BigInteger bigTagValue) { + if (bigTagValue == null) { + throw new ArgumentNullException(nameof(bigTagValue)); + } + return this.HasTag(EInteger.FromBytes( + bigTagValue.toBytes(true), + true)); + } + + /// + public bool HasTag(EInteger bigTagValue) { + if (bigTagValue == null) { + throw new ArgumentNullException(nameof(bigTagValue)); + } + if (bigTagValue.Sign < 0) { + throw new ArgumentException("doesn't satisfy bigTagValue.Sign>= 0"); + } + EInteger[] bigTags = this.GetAllTags(); + foreach (EInteger bigTag in bigTags) { + if (bigTagValue.Equals(bigTag)) { + return true; + } + } + return false; + } + + /// + public CBORObject Insert(int index, object valueOb) { + if (this.ItemType == CBORObjectTypeArray) { + CBORObject mapValue; + IList list = this.AsList(); + if (index < 0 || index > list.Count) { + throw new ArgumentException("index"); + } + if (valueOb == null) { + mapValue = CBORObject.Null; + } else { + mapValue = valueOb as CBORObject; + mapValue = mapValue ?? CBORObject.FromObject(valueOb); + } + list.Insert(index, mapValue); + } else { + throw new InvalidOperationException("Not an array"); + } + return this; + } + + /// + public bool IsInfinity() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return cn != null && cn.IsInfinity(this.ThisItem); + } + + /// + public bool IsNaN() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return cn != null && cn.IsNaN(this.ThisItem); + } + + /// + public bool IsNegativeInfinity() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return cn != null && cn.IsNegativeInfinity(this.ThisItem); + } + + /// + public bool IsPositiveInfinity() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + return cn != null && cn.IsPositiveInfinity(this.ThisItem); + } + + /// + public CBORObject Negate() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("This object is not a number."); + } + object newItem = cn.Negate(this.ThisItem); + if (newItem is EDecimal) { + return CBORObject.FromObject((EDecimal)newItem); + } + if (newItem is EInteger) { + return CBORObject.FromObject((EInteger)newItem); + } + if (newItem is EFloat) { + return CBORObject.FromObject((EFloat)newItem); + } + var rat = newItem as ERational; + return (rat != null) ? CBORObject.FromObject(rat) : + CBORObject.FromObject(newItem); + } + + /// + public void Clear() { + if (this.ItemType == CBORObjectTypeArray) { + IList list = this.AsList(); + list.Clear(); + } else if (this.ItemType == CBORObjectTypeMap) { + IDictionary dict = this.AsMap(); + dict.Clear(); + } else { + throw new InvalidOperationException("Not a map or array"); + } + } + + /// + public bool Remove(object obj) { + if (obj == null) { + throw new ArgumentNullException(nameof(obj)); + } + // TODO: Convert null to CBORObject.Null in next major version + return this.Remove(CBORObject.FromObject(obj)); + } + + /// + public bool RemoveAt(int index) { + if (this.ItemType != CBORObjectTypeArray) { + throw new InvalidOperationException("Not an array"); + } + if (index < 0 || index >= this.Count) { + return false; + } + IList list = this.AsList(); + list.RemoveAt(index); + return true; + } + + /// + public bool Remove(CBORObject obj) { + if (obj == null) { + throw new ArgumentNullException(nameof(obj)); + } + if (this.ItemType == CBORObjectTypeMap) { + IDictionary dict = this.AsMap(); + bool hasKey = dict.ContainsKey(obj); + if (hasKey) { + dict.Remove(obj); + return true; + } + return false; + } + if (this.ItemType == CBORObjectTypeArray) { + IList list = this.AsList(); + return list.Remove(obj); + } + throw new InvalidOperationException("Not a map or array"); + } + + /// + public CBORObject Set(object key, object valueOb) { + if (this.ItemType == CBORObjectTypeMap) { + CBORObject mapKey; + CBORObject mapValue; + if (key == null) { + mapKey = CBORObject.Null; + } else { + mapKey = key as CBORObject; + mapKey = mapKey ?? CBORObject.FromObject(key); + } + if (valueOb == null) { + mapValue = CBORObject.Null; + } else { + mapValue = valueOb as CBORObject; + mapValue = mapValue ?? CBORObject.FromObject(valueOb); + } + IDictionary map = this.AsMap(); + if (map.ContainsKey(mapKey)) { + map[mapKey] = mapValue; + } else { + map.Add(mapKey, mapValue); + } + } else { + throw new InvalidOperationException("Not a map"); + } + return this; + } + + /// + public string ToJSONString() { + return this.ToJSONString(JSONOptions.Default); + } + + /// + public string ToJSONString(JSONOptions options) { + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + int type = this.ItemType; + switch (type) { + case CBORObjectTypeSimpleValue: { + return this.IsTrue ? "true" : (this.IsFalse ? "false" : "null"); + } + case CBORObjectTypeInteger: { + return CBORUtilities.LongToString((long)this.ThisItem); + } + default: { + var sb = new StringBuilder(); + try { + CBORJson.WriteJSONToInternal(this, new StringOutput(sb), options); + } catch (IOException ex) { + // This is truly exceptional + throw new InvalidOperationException("Internal error", ex); + } + return sb.ToString(); + } + } + } + + /// + public override string ToString() { + StringBuilder sb = null; + string simvalue = null; + int type = this.ItemType; + if (this.IsTagged) { + if (sb == null) { + if (type == CBORObjectTypeTextString) { + // The default capacity of StringBuilder may be too small + // for many strings, so set a suggested capacity + // explicitly + string str = this.AsString(); + sb = new StringBuilder(Math.Min(str.Length, 4096) + 16); + } else { + sb = new StringBuilder(); + } + } + this.AppendOpeningTags(sb); + } + switch (type) { + case CBORObjectTypeSimpleValue: { + if (this.IsTrue) { + simvalue = "true"; + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + } else if (this.IsFalse) { + simvalue = "false"; + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + } else if (this.IsNull) { + simvalue = "null"; + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + } else if (this.IsUndefined) { + simvalue = "undefined"; + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + } else { + sb = sb ?? (new StringBuilder()); + sb.Append("simple("); + var thisItemInt = (int)this.ThisItem; + sb.Append( + CBORUtilities.LongToString(thisItemInt)); + sb.Append(")"); + } + + break; + } + case CBORObjectTypeSingle: { + var f = (float)this.ThisItem; + simvalue = Single.IsNegativeInfinity(f) ? "-Infinity" : + (Single.IsPositiveInfinity(f) ? "Infinity" : (Single.IsNaN(f) ? + "NaN" : TrimDotZero(CBORUtilities.SingleToString(f)))); + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + break; + } + case CBORObjectTypeDouble: { + var f = (double)this.ThisItem; + simvalue = Double.IsNegativeInfinity(f) ? "-Infinity" : + (Double.IsPositiveInfinity(f) ? "Infinity" : (Double.IsNaN(f) ? + "NaN" : TrimDotZero(CBORUtilities.DoubleToString(f)))); + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + break; + } + case CBORObjectTypeExtendedFloat: { + simvalue = ExtendedToString((EFloat)this.ThisItem); + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + break; + } + case CBORObjectTypeInteger: { + var v = (long)this.ThisItem; + simvalue = CBORUtilities.LongToString(v); + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + break; + } + case CBORObjectTypeBigInteger: { + simvalue = ((EInteger)this.ThisItem).ToString(); + if (sb == null) { + return simvalue; + } + sb.Append(simvalue); + break; + } + case CBORObjectTypeByteString: { + sb = sb ?? (new StringBuilder()); + sb.Append("h'"); + CBORUtilities.ToBase16(sb, (byte[])this.ThisItem); + sb.Append("'"); + break; + } + case CBORObjectTypeTextString: { + if (sb == null) { + return "\"" + this.AsString() + "\""; + } + sb.Append('\"'); + sb.Append(this.AsString()); + sb.Append('\"'); + break; + } + case CBORObjectTypeArray: { + sb = sb ?? (new StringBuilder()); + var first = true; + sb.Append("["); + foreach (CBORObject i in this.AsList()) { + if (!first) { + sb.Append(", "); + } + sb.Append(i.ToString()); + first = false; + } + sb.Append("]"); + break; + } + case CBORObjectTypeMap: { + sb = sb ?? (new StringBuilder()); + var first = true; + sb.Append("{"); + IDictionary map = this.AsMap(); + foreach (KeyValuePair entry in map) { + CBORObject key = entry.Key; + CBORObject value = entry.Value; + if (!first) { + sb.Append(", "); + } + sb.Append(key.ToString()); + sb.Append(": "); + sb.Append(value.ToString()); + first = false; + } + sb.Append("}"); + break; + } + default: { + if (sb == null) { + return this.ThisItem.ToString(); + } + sb.Append(this.ThisItem.ToString()); + break; + } + } + + if (this.IsTagged) { + this.AppendClosingTags(sb); + } + return sb.ToString(); + } + + /// + public CBORObject Untag() { + CBORObject curobject = this; + while (curobject.itemtypeValue == CBORObjectTypeTagged) { + curobject = (CBORObject)curobject.itemValue; + } + return curobject; + } + + /// + public CBORObject UntagOne() { + return (this.itemtypeValue == CBORObjectTypeTagged) ? + ((CBORObject)this.itemValue) : this; + } + + /// + public void WriteJSONTo(Stream outputStream) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); + } + CBORJson.WriteJSONToInternal( + this, + new StringOutput(outputStream), + JSONOptions.Default); + } + + /// + public void WriteJSONTo(Stream outputStream, JSONOptions options) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); + } + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + CBORJson.WriteJSONToInternal( + this, + new StringOutput(outputStream), + options); + } + + /// + public static int WriteValue( + Stream outputStream, + int majorType, + long value) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); + } + if (majorType < 0) { + throw new ArgumentException("majorType (" + majorType + + ") is less than 0"); + } + if (majorType > 7) { + throw new ArgumentException("majorType (" + majorType + + ") is more than 7"); + } + if (value < 0) { + throw new ArgumentException("value (" + value + + ") is less than 0"); + } + if (majorType == 7) { + if (value > 255) { + throw new ArgumentException("value (" + value + + ") is more than 255"); + } + if (value <= 23) { + outputStream.WriteByte((byte)(0xe0 + (int)value)); + return 1; + } else if (value < 32) { + throw new ArgumentException("value is from 24 to 31 and major type is 7"); + } else { + outputStream.WriteByte((byte)0xf8); + outputStream.WriteByte((byte)value); + return 2; + } + } else { + return WritePositiveInt64(majorType, value, outputStream); + } + } + + /// + public static int WriteValue( + Stream outputStream, + int majorType, + int value) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); + } + if (majorType < 0) { + throw new ArgumentException("majorType (" + majorType + + ") is less than 0"); + } + if (majorType > 7) { + throw new ArgumentException("majorType (" + majorType + + ") is more than 7"); + } + if (value < 0) { + throw new ArgumentException("value (" + value + + ") is less than 0"); + } + if (majorType == 7) { + if (value > 255) { + throw new ArgumentException("value (" + value + + ") is more than 255"); + } + if (value <= 23) { + outputStream.WriteByte((byte)(0xe0 + value)); + return 1; + } else if (value < 32) { + throw new ArgumentException("value is from 24 to 31 and major type is 7"); + } else { + outputStream.WriteByte((byte)0xf8); + outputStream.WriteByte((byte)value); + return 2; + } + } else { + return WritePositiveInt(majorType, value, outputStream); + } + } + + /// + public static int WriteValue( + Stream outputStream, + int majorType, + EInteger bigintValue) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); + } + if (bigintValue == null) { + throw new ArgumentNullException(nameof(bigintValue)); + } + if (bigintValue.Sign < 0) { + throw new ArgumentException("tagEInt's sign (" + bigintValue.Sign + + ") is less than 0"); + } + if (bigintValue.CompareTo(UInt64MaxValue) > 0) { + throw new ArgumentException( + "tag more than 18446744073709551615 (" + bigintValue + ")"); + } + if (bigintValue.CompareTo(Int64MaxValue) <= 0) { + return WriteValue( + outputStream, + majorType, + bigintValue.ToInt64Checked()); + } + long longVal = bigintValue.ToInt64Unchecked(); + var highbyte = (int)((longVal >> 56) & 0xff); + if (majorType < 0) { + throw new ArgumentException("majorType (" + majorType + + ") is less than 0"); + } + if (majorType > 7) { + throw new ArgumentException("majorType (" + majorType + + ") is more than 7"); + } + if (majorType == 7) { + throw new ArgumentException("majorType is 7 and value is greater than 255"); + } + byte[] bytes = new[] { (byte)(27 | (majorType << 5)), (byte)highbyte, + (byte)((longVal >> 48) & 0xff), (byte)((longVal >> 40) & 0xff), + (byte)((longVal >> 32) & 0xff), (byte)((longVal >> 24) & 0xff), + (byte)((longVal >> 16) & 0xff), (byte)((longVal >> 8) & 0xff), + (byte)(longVal & 0xff) }; + outputStream.Write(bytes, 0, bytes.Length); + return bytes.Length; + } + + /// + public void WriteTo(Stream stream) { + this.WriteTo(stream, new CBOREncodeOptions(true, true)); + } + + /// + public void WriteTo(Stream stream, CBOREncodeOptions options) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (options == null) { + throw new ArgumentNullException(nameof(options)); + } + if (options.Ctap2Canonical) { + byte[] bytes = CBORCanonical.CtapCanonicalEncode(this); + stream.Write(bytes, 0, bytes.Length); + return; + } + this.WriteTags(stream); + int type = this.ItemType; + switch (type) { + case CBORObjectTypeInteger: { + Write((long)this.ThisItem, stream); + break; + } + case CBORObjectTypeBigInteger: { + Write((EInteger)this.ThisItem, stream); + break; + } + case CBORObjectTypeByteString: { + var arr = (byte[])this.ThisItem; + WritePositiveInt( + (this.ItemType == CBORObjectTypeByteString) ? 2 : 3, + arr.Length, + stream); + stream.Write(arr, 0, arr.Length); + break; + } + case CBORObjectTypeTextString: { + Write((string)this.ThisItem, stream, options); + break; + } + case CBORObjectTypeArray: { + WriteObjectArray(this.AsList(), stream, options); + break; + } + case CBORObjectTypeExtendedDecimal: { + var dec = (EDecimal)this.ThisItem; + Write(dec, stream); + break; + } + case CBORObjectTypeExtendedFloat: { + var flo = (EFloat)this.ThisItem; + Write(flo, stream); + break; + } + case CBORObjectTypeExtendedRational: { + var flo = (ERational)this.ThisItem; + Write(flo, stream); + break; + } + case CBORObjectTypeMap: { + WriteObjectMap(this.AsMap(), stream, options); + break; + } + case CBORObjectTypeSimpleValue: { + var value = (int)this.ThisItem; + if (value < 24) { + stream.WriteByte((byte)(0xe0 + value)); + } else { +#if DEBUG + if (value < 32) { + throw new ArgumentException("value (" + value + + ") is less than " + "32"); + } +#endif + + stream.WriteByte(0xf8); + stream.WriteByte((byte)value); + } + + break; + } + case CBORObjectTypeSingle: { + Write((float)this.ThisItem, stream); + break; + } + case CBORObjectTypeDouble: { + Write((double)this.ThisItem, stream); + break; + } + default: { + throw new ArgumentException("Unexpected data type"); + } + } + } + + #pragma warning disable 618 + internal static ICBORTag FindTagConverter(EInteger bigintTag) { + if (TagHandlersEmpty()) { + AddTagHandler((EInteger)2, new CBORTag2()); + AddTagHandler((EInteger)3, new CBORTag3()); + AddTagHandler((EInteger)4, new CBORTag4()); + AddTagHandler((EInteger)5, new CBORTag5()); + AddTagHandler((EInteger)264, new CBORTag4(true)); + AddTagHandler((EInteger)265, new CBORTag5(true)); + AddTagHandler((EInteger)25, new CBORTagUnsigned()); + AddTagHandler((EInteger)29, new CBORTagUnsigned()); + AddTagHandler((EInteger)256, new CBORTagAny()); + AddTagHandler(EInteger.Zero, new CBORTag0()); + AddTagHandler((EInteger)32, new CBORTag32()); + AddTagHandler((EInteger)33, new CBORTagGenericString()); + AddTagHandler((EInteger)34, new CBORTagGenericString()); + AddTagHandler((EInteger)35, new CBORTagGenericString()); + AddTagHandler((EInteger)36, new CBORTagGenericString()); + AddTagHandler((EInteger)37, new CBORTag37()); + AddTagHandler((EInteger)30, new CBORTag30()); + } + lock (ValueTagHandlers) { + if (ValueTagHandlers.ContainsKey(bigintTag)) { + return ValueTagHandlers[bigintTag]; + } +#if DEBUG + if (bigintTag.Equals((EInteger)2)) { + throw new InvalidOperationException("Expected valid tag handler"); + } +#endif + return null; + } + } + + internal static ICBORTag FindTagConverterLong(long tagLong) { + return FindTagConverter((EInteger)tagLong); + } + #pragma warning restore 618 + + internal static CBORObject FromRaw(string str) { + return new CBORObject(CBORObjectTypeTextString, str); + } + + internal static CBORObject FromRaw(IList list) { + return new CBORObject(CBORObjectTypeArray, list); + } + + internal static CBORObject FromRaw(IDictionary + map) { + return new CBORObject(CBORObjectTypeMap, map); + } + + internal static int GetExpectedLength(int value) { + return ValueExpectedLengths[value]; + } + + // Generate a CBOR object for head bytes with fixed length. + // Note that this function assumes that the length of the data + // was already checked. + internal static CBORObject GetFixedLengthObject( + int firstbyte, + byte[] data) { + CBORObject fixedObj = valueFixedObjects[firstbyte]; + if (fixedObj != null) { + return fixedObj; + } + int majortype = firstbyte >> 5; + if (firstbyte >= 0x61 && firstbyte < 0x78) { + // text string length 1 to 23 + string s = GetOptimizedStringIfShortAscii(data, 0); + if (s != null) { + return new CBORObject(CBORObjectTypeTextString, s); + } + } + if ((firstbyte & 0x1c) == 0x18) { + // contains 1 to 8 extra bytes of additional information + long uadditional = 0; + switch (firstbyte & 0x1f) { + case 24: + uadditional = (int)(data[1] & (int)0xff); + break; + case 25: + uadditional = ((long)(data[1] & (long)0xff)) << 8; + uadditional |= (long)(data[2] & (long)0xff); + break; + case 26: + uadditional = ((long)(data[1] & (long)0xff)) << 24; + uadditional |= ((long)(data[2] & (long)0xff)) << 16; + uadditional |= ((long)(data[3] & (long)0xff)) << 8; + uadditional |= (long)(data[4] & (long)0xff); + break; + case 27: + uadditional = ((long)(data[1] & (long)0xff)) << 56; + uadditional |= ((long)(data[2] & (long)0xff)) << 48; + uadditional |= ((long)(data[3] & (long)0xff)) << 40; + uadditional |= ((long)(data[4] & (long)0xff)) << 32; + uadditional |= ((long)(data[5] & (long)0xff)) << 24; + uadditional |= ((long)(data[6] & (long)0xff)) << 16; + uadditional |= ((long)(data[7] & (long)0xff)) << 8; + uadditional |= (long)(data[8] & (long)0xff); + break; + default: + throw new CBORException("Unexpected data encountered"); + } + switch (majortype) { + case 0: + if ((uadditional >> 63) == 0) { + // use only if additional's top bit isn't set + // (additional is a signed long) + return new CBORObject(CBORObjectTypeInteger, uadditional); + } else { + int low = unchecked((int)((uadditional) & 0xffffffffL)); + int high = unchecked((int)((uadditional >> 32) & 0xffffffffL)); + return FromObject(LowHighToEInteger(low, high)); + } + case 1: + if ((uadditional >> 63) == 0) { + // use only if additional's top bit isn't set + // (additional is a signed long) + return new CBORObject(CBORObjectTypeInteger, -1 - uadditional); + } else { + int low = unchecked((int)((uadditional) & 0xffffffffL)); + int high = unchecked((int)((uadditional >> 32) & 0xffffffffL)); + EInteger bigintAdditional = LowHighToEInteger(low, high); + EInteger minusOne = -EInteger.One; + bigintAdditional = minusOne - (EInteger)bigintAdditional; + return FromObject(bigintAdditional); + } + case 7: + if (firstbyte == 0xf9) { + return new CBORObject( + CBORObjectTypeSingle, + CBORUtilities.HalfPrecisionToSingle(unchecked((int)uadditional))); + } + if (firstbyte == 0xfa) { + float flt = BitConverter.ToSingle( + BitConverter.GetBytes((int)unchecked((int)uadditional)), + 0); + return new CBORObject( + CBORObjectTypeSingle, + flt); + } + if (firstbyte == 0xfb) { + double flt = BitConverter.ToDouble( + BitConverter.GetBytes((long)uadditional), + 0); + return new CBORObject( + CBORObjectTypeDouble, + flt); + } + if (firstbyte == 0xf8) { + if ((int)uadditional < 32) { + throw new CBORException("Invalid overlong simple value"); + } + return new CBORObject( + CBORObjectTypeSimpleValue, + (int)uadditional); + } + throw new CBORException("Unexpected data encountered"); + default: throw new CBORException("Unexpected data encountered"); + } + } + if (majortype == 2) { // short byte string + var ret = new byte[firstbyte - 0x40]; + Array.Copy(data, 1, ret, 0, firstbyte - 0x40); + return new CBORObject(CBORObjectTypeByteString, ret); + } + if (majortype == 3) { // short text string + var ret = new StringBuilder(firstbyte - 0x60); + DataUtilities.ReadUtf8FromBytes(data, 1, firstbyte - 0x60, ret, false); + return new CBORObject(CBORObjectTypeTextString, ret.ToString()); + } + if (firstbyte == 0x80) { + // empty array + return FromObject(new List()); + } + if (firstbyte == 0xa0) { + // empty map + return FromObject(new Dictionary()); + } + throw new CBORException("Unexpected data encountered"); + } + + internal static CBORObject GetFixedObject(int value) { + return valueFixedObjects[value]; + } + + internal static ICBORNumber GetNumberInterface(int type) { + return NumberInterfaces[type]; + } + + internal static string TrimDotZero(string str) { + return (str.Length > 2 && str[str.Length - 1] == '0' && str[str.Length + - 2] == '.') ? str.Substring(0, str.Length - 2) : + str; + } + + internal IList AsList() { + return (IList)this.ThisItem; + } + + internal IDictionary AsMap() { + return (IDictionary)this.ThisItem; + } + /* + internal void Redefine(CBORObject cbor) { +#if DEBUG + if (cbor == null) { + throw new ArgumentNullException(nameof(cbor)); + } +#endif + this.itemtypeValue = cbor.itemtypeValue; + this.tagLow = cbor.tagLow; + this.tagHigh = cbor.tagHigh; + this.itemValue = cbor.itemValue; + } */ + + private static bool BigIntFits(EInteger bigint) { + return bigint.GetSignedBitLength() <= 64; + } + + private static bool CBORArrayEquals( + IList listA, + IList listB) { + if (listA == null) { + return listB == null; + } + if (listB == null) { + return false; + } + int listACount = listA.Count; + int listBCount = listB.Count; + if (listACount != listBCount) { + return false; + } + for (var i = 0; i < listACount; ++i) { + CBORObject itemA = listA[i]; + CBORObject itemB = listB[i]; + if (!(itemA == null ? itemB == null : itemA.Equals(itemB))) { + return false; + } + } + return true; + } + + private static int CBORArrayHashCode(IList list) { + if (list == null) { + return 0; + } + var ret = 19; + int count = list.Count; + unchecked { + ret = (ret * 31) + count; + for (var i = 0; i < count; ++i) { + ret = (ret * 31) + list[i].GetHashCode(); + } + } + return ret; + } + + private static int StringHashCode(string str) { + if (str == null) { + return 0; + } + var ret = 19; + int count = str.Length; + unchecked { + ret = (ret * 31) + count; + for (var i = 0; i < count; ++i) { + ret = (ret * 31) + (int)str[i]; + } + } + return ret; + } + + private static bool CBORMapEquals( + IDictionary mapA, + IDictionary mapB) { + if (mapA == null) { + return mapB == null; + } + if (mapB == null) { + return false; + } + if (mapA.Count != mapB.Count) { + return false; + } + foreach (KeyValuePair kvp in mapA) { + CBORObject valueB = null; + bool hasKey = mapB.TryGetValue(kvp.Key, out valueB); + if (hasKey) { + CBORObject valueA = kvp.Value; + if (!(valueA == null ? valueB == null : valueA.Equals(valueB))) { + return false; + } + } else { + return false; + } + } + return true; + } + + private static int CBORMapHashCode(IDictionary a) { + // To simplify matters, we use just the count of + // the map as the basis for the hash code. More complicated + // hash code calculation would generally involve defining + // how CBORObjects ought to be compared (since a stable + // sort order is necessary for two equal maps to have the + // same hash code), which is much too difficult to do. + return unchecked(a.Count.GetHashCode() * 19); + } + + private static void CheckCBORLength( + long expectedLength, + long actualLength) { + if (actualLength < expectedLength) { + throw new CBORException("Premature end of data"); + } + if (actualLength > expectedLength) { + throw new CBORException("Too many bytes"); + } + } + + private static void CheckCBORLength(int expectedLength, int actualLength) { + if (actualLength < expectedLength) { + throw new CBORException("Premature end of data"); + } + if (actualLength > expectedLength) { + throw new CBORException("Too many bytes"); + } + } + + private static CBORObject ConvertWithConverter(object obj) { +#if DEBUG + if (obj == null) { + throw new ArgumentNullException(nameof(obj)); + } +#endif + Object type = obj.GetType(); + ConverterInfo convinfo = null; + lock (ValueConverters) { + if (ValueConverters.Count == 0) { + CBORTag0.AddConverter(); + CBORTag37.AddConverter(); + CBORTag32.AddConverter(); + } + if (ValueConverters.ContainsKey(type)) { + convinfo = ValueConverters[type]; + } else { + return null; + } + } + if (convinfo == null) { + return null; + } + return (CBORObject)PropertyMap.InvokeOneArgumentMethod( + convinfo.ToObject, + convinfo.Converter, + obj); + } + + private static string ExtendedToString(EFloat ef) { + if (ef.IsFinite && (ef.Exponent.CompareTo((EInteger)2500) > 0 || + ef.Exponent.CompareTo((EInteger)(-2500)) < 0)) { + // It can take very long to convert a number with a very high + // or very low exponent to a decimal string, so do this instead + return ef.Mantissa + "p" + ef.Exponent; + } + return ef.ToString(); + } + + [Obsolete] + private static ICBORTag FindTagConverter(int tag) { + return FindTagConverter((EInteger)tag); + } + + private static byte[] GetOptimizedBytesIfShortAscii( + string str, + int tagbyteInt) { + byte[] bytes; + if (str.Length <= 255) { + // The strings will usually be short ASCII strings, so + // use this optimization + var offset = 0; + int length = str.Length; + int extra = (length < 24) ? 1 : 2; + if (tagbyteInt >= 0) { + ++extra; + } + bytes = new byte[length + extra]; + if (tagbyteInt >= 0) { + bytes[offset] = (byte)tagbyteInt; + ++offset; + } + if (length < 24) { + bytes[offset] = (byte)(0x60 + str.Length); + ++offset; + } else { + bytes[offset] = (byte)0x78; + bytes[offset + 1] = (byte)str.Length; + offset += 2; + } + var issimple = true; + for (var i = 0; i < str.Length; ++i) { + char c = str[i]; + if (c >= 0x80) { + issimple = false; + break; + } + bytes[i + offset] = unchecked((byte)c); + } + if (issimple) { + return bytes; + } + } + return null; + } + + private static string GetOptimizedStringIfShortAscii( + byte[] data, + int offset) { + int length = data.Length; + if (length > offset) { + var nextbyte = (int)(data[offset] & (int)0xff); + if (nextbyte >= 0x60 && nextbyte < 0x78) { + int offsetp1 = 1 + offset; + // Check for type 3 string of short length + int rightLength = offsetp1 + (nextbyte - 0x60); + CheckCBORLength(rightLength, length); + // Check for all ASCII text + for (int i = offsetp1; i < length; ++i) { + if ((data[i] & ((byte)0x80)) != 0) { + return null; + } + } + // All ASCII text, so convert to a string + // from a char array without having to + // convert from UTF-8 first + var c = new char[length - offsetp1]; + for (int i = offsetp1; i < length; ++i) { + c[i - offsetp1] = (char)(data[i] & (int)0xff); + } + return new String(c); + } + } + return null; + } + + private static byte[] GetPositiveInt64Bytes(int type, long value) { + if (value < 0) { + throw new ArgumentException("value (" + value + ") is less than " + + "0"); + } + if (value < 24) { + return new[] { (byte)((byte)value | (byte)(type << 5)) }; + } + if (value <= 0xffL) { + return new[] { (byte)(24 | (type << 5)), (byte)(value & 0xff) + }; + } + if (value <= 0xffffL) { + return new[] { (byte)(25 | (type << 5)), + (byte)((value >> 8) & 0xff), (byte)(value & 0xff) + }; + } + if (value <= 0xffffffffL) { + return new[] { (byte)(26 | (type << 5)), + (byte)((value >> 24) & 0xff), (byte)((value >> 16) & 0xff), + (byte)((value >> 8) & 0xff), (byte)(value & 0xff) + }; + } + return new[] { (byte)(27 | (type << 5)), (byte)((value >> 56) & 0xff), + (byte)((value >> 48) & 0xff), (byte)((value >> 40) & 0xff), + (byte)((value >> 32) & 0xff), (byte)((value >> 24) & 0xff), + (byte)((value >> 16) & 0xff), (byte)((value >> 8) & 0xff), + (byte)(value & 0xff) }; + } + + private static byte[] GetPositiveIntBytes(int type, int value) { + if (value < 0) { + throw new ArgumentException("value (" + value + ") is less than " + + "0"); + } + if (value < 24) { + return new[] { (byte)((byte)value | (byte)(type << 5)) }; + } + if (value <= 0xff) { + return new[] { (byte)(24 | (type << 5)), (byte)(value & 0xff) + }; + } + if (value <= 0xffff) { + return new[] { (byte)(25 | (type << 5)), + (byte)((value >> 8) & 0xff), (byte)(value & 0xff) + }; + } + return new[] { (byte)(26 | (type << 5)), (byte)((value >> 24) & 0xff), + (byte)((value >> 16) & 0xff), (byte)((value >> 8) & 0xff), + (byte)(value & 0xff) }; + } + + private static int GetSignInternal(int type, object obj) { + ICBORNumber cn = NumberInterfaces[type]; + return cn == null ? 2 : cn.Sign(obj); + } + // Initialize fixed values for certain + // head bytes + private static CBORObject[] InitializeFixedObjects() { + valueFixedObjects = new CBORObject[256]; + for (var i = 0; i < 0x18; ++i) { + valueFixedObjects[i] = new CBORObject(CBORObjectTypeInteger, (long)i); + } + for (int i = 0x20; i < 0x38; ++i) { + valueFixedObjects[i] = new CBORObject( + CBORObjectTypeInteger, + (long)(-1 - (i - 0x20))); + } + valueFixedObjects[0x60] = new CBORObject( + CBORObjectTypeTextString, + String.Empty); + for (int i = 0xe0; i < 0xf8; ++i) { + valueFixedObjects[i] = new CBORObject( + CBORObjectTypeSimpleValue, + (int)(i - 0xe0)); + } + return valueFixedObjects; + } + + private static int ListCompare( + IList listA, + IList listB) { + if (listA == null) { + return (listB == null) ? 0 : -1; + } + if (listB == null) { + return 1; + } + int listACount = listA.Count; + int listBCount = listB.Count; + int c = Math.Min(listACount, listBCount); + for (var i = 0; i < c; ++i) { + int cmp = listA[i].CompareTo(listB[i]); + if (cmp != 0) { + return cmp; + } + } + return (listACount != listBCount) ? ((listACount < listBCount) ? -1 : 1) : + 0; + } + + private static EInteger LowHighToEInteger(int tagLow, int tagHigh) { + byte[] uabytes = null; + if (tagHigh != 0) { + uabytes = new byte[9]; + uabytes[7] = (byte)((tagHigh >> 24) & 0xff); + uabytes[6] = (byte)((tagHigh >> 16) & 0xff); + uabytes[5] = (byte)((tagHigh >> 8) & 0xff); + uabytes[4] = (byte)(tagHigh & 0xff); + uabytes[3] = (byte)((tagLow >> 24) & 0xff); + uabytes[2] = (byte)((tagLow >> 16) & 0xff); + uabytes[1] = (byte)((tagLow >> 8) & 0xff); + uabytes[0] = (byte)(tagLow & 0xff); + uabytes[8] = 0; + return EInteger.FromBytes(uabytes, true); + } + if (tagLow != 0) { + uabytes = new byte[5]; + uabytes[3] = (byte)((tagLow >> 24) & 0xff); + uabytes[2] = (byte)((tagLow >> 16) & 0xff); + uabytes[1] = (byte)((tagLow >> 8) & 0xff); + uabytes[0] = (byte)(tagLow & 0xff); + uabytes[4] = 0; + return EInteger.FromBytes(uabytes, true); + } + return EInteger.Zero; + } + + private static int MapCompare( + IDictionary mapA, + IDictionary mapB) { + if (mapA == null) { + return (mapB == null) ? 0 : -1; + } + if (mapB == null) { + return 1; + } + if (mapA == mapB) { + return 0; + } + int listACount = mapA.Count; + int listBCount = mapB.Count; + if (listACount == 0 && listBCount == 0) { + return 0; + } + if (listACount == 0) { + return -1; + } + if (listBCount == 0) { + return 1; + } + var sortedASet = new List(mapA.Keys); + var sortedBSet = new List(mapB.Keys); + sortedASet.Sort(); + sortedBSet.Sort(); + listACount = sortedASet.Count; + listBCount = sortedBSet.Count; + int minCount = Math.Min(listACount, listBCount); + // Compare the keys + for (var i = 0; i < minCount; ++i) { + CBORObject itemA = sortedASet[i]; + CBORObject itemB = sortedBSet[i]; + if (itemA == null) { + return -1; + } + int cmp = itemA.CompareTo(itemB); + if (cmp != 0) { + return cmp; + } + } + if (listACount == listBCount) { + // Both maps have the same keys, so compare their values + for (var i = 0; i < minCount; ++i) { + CBORObject keyA = sortedASet[i]; + CBORObject keyB = sortedBSet[i]; + int cmp = mapA[keyA].CompareTo(mapB[keyB]); + if (cmp != 0) { + return cmp; + } + } + return 0; + } + return (listACount > listBCount) ? 1 : -1; + } + + private static IList PushObject( + IList stack, + object parent, + object child) { + if (stack == null) { + stack = new List(); + stack.Add(parent); + } + foreach (object o in stack) { + if (o == child) { + throw new ArgumentException("Circular reference in data structure"); + } + } + stack.Add(child); + return stack; + } + + #pragma warning disable 618 + private static bool TagHandlersEmpty() { + lock (ValueTagHandlers) { + return ValueTagHandlers.Count == 0; + } + } + #pragma warning restore 618 + + private static int TagsCompare(EInteger[] tagsA, EInteger[] tagsB) { + if (tagsA == null) { + return (tagsB == null) ? 0 : -1; + } + if (tagsB == null) { + return 1; + } + int listACount = tagsA.Length; + int listBCount = tagsB.Length; + int c = Math.Min(listACount, listBCount); + for (var i = 0; i < c; ++i) { + int cmp = tagsA[i].CompareTo(tagsB[i]); + if (cmp != 0) { + return cmp; + } + } + return (listACount != listBCount) ? ((listACount < listBCount) ? -1 : 1) : + 0; + } + + private static IList WriteChildObject( + object parentThisItem, + CBORObject child, + Stream outputStream, + IList stack, + CBOREncodeOptions options) { + if (child == null) { + outputStream.WriteByte(0xf6); + } else { + int type = child.ItemType; + if (type == CBORObjectTypeArray) { + stack = PushObject(stack, parentThisItem, child.ThisItem); + child.WriteTags(outputStream); + WriteObjectArray(child.AsList(), outputStream, stack, options); + stack.RemoveAt(stack.Count - 1); + } else if (type == CBORObjectTypeMap) { + stack = PushObject(stack, parentThisItem, child.ThisItem); + child.WriteTags(outputStream); + WriteObjectMap(child.AsMap(), outputStream, stack, options); + stack.RemoveAt(stack.Count - 1); + } else { + child.WriteTo(outputStream, options); + } + } + return stack; + } + + private static void WriteObjectArray( + IList list, + Stream outputStream, + CBOREncodeOptions options) { + WriteObjectArray(list, outputStream, null, options); + } + + private static void WriteObjectArray( + IList list, + Stream outputStream, + IList stack, + CBOREncodeOptions options) { + object thisObj = list; + WritePositiveInt(4, list.Count, outputStream); + foreach (CBORObject i in list) { + stack = WriteChildObject(thisObj, i, outputStream, stack, options); + } + } + + private static void WriteObjectMap( + IDictionary map, + Stream outputStream, + CBOREncodeOptions options) { + WriteObjectMap(map, outputStream, null, options); + } + + private static void WriteObjectMap( + IDictionary map, + Stream outputStream, + IList stack, + CBOREncodeOptions options) { + object thisObj = map; + WritePositiveInt(5, map.Count, outputStream); + foreach (KeyValuePair entry in map) { + CBORObject key = entry.Key; + CBORObject value = entry.Value; + stack = WriteChildObject( + thisObj, + key, + outputStream, + stack, + options); + stack = WriteChildObject( + thisObj, + value, + outputStream, + stack, + options); + } + } + + private static int WritePositiveInt(int type, int value, Stream s) { + byte[] bytes = GetPositiveIntBytes(type, value); + s.Write(bytes, 0, bytes.Length); + return bytes.Length; + } + + private static int WritePositiveInt64(int type, long value, Stream s) { + byte[] bytes = GetPositiveInt64Bytes(type, value); + s.Write(bytes, 0, bytes.Length); + return bytes.Length; + } + + private static void WriteStreamedString(string str, Stream stream) { + byte[] bytes; + bytes = GetOptimizedBytesIfShortAscii(str, -1); + if (bytes != null) { + stream.Write(bytes, 0, bytes.Length); + return; + } + bytes = new byte[StreamedStringBufferLength]; + var byteIndex = 0; + var streaming = false; + for (int index = 0; index < str.Length; ++index) { + int c = str[index]; + if (c <= 0x7f) { + if (byteIndex >= StreamedStringBufferLength) { + // Write bytes retrieved so far + if (!streaming) { + stream.WriteByte((byte)0x7f); + } + WritePositiveInt(3, byteIndex, stream); + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + streaming = true; + } + bytes[byteIndex++] = (byte)c; + } else if (c <= 0x7ff) { + if (byteIndex + 2 > StreamedStringBufferLength) { + // Write bytes retrieved so far - the next three bytes + // would exceed the length, and the CBOR spec forbids + // splitting characters when generating text strings + if (!streaming) { + stream.WriteByte((byte)0x7f); + } + WritePositiveInt(3, byteIndex, stream); + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + streaming = true; + } + bytes[byteIndex++] = (byte)(0xc0 | ((c >> 6) & 0x1f)); + bytes[byteIndex++] = (byte)(0x80 | (c & 0x3f)); + } else { + if ((c & 0xfc00) == 0xd800 && index + 1 < str.Length && + (str[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (str[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + // unpaired surrogate, write U + FFFD instead + c = 0xfffd; + } + if (c <= 0xffff) { + if (byteIndex + 3 > StreamedStringBufferLength) { + // Write bytes retrieved so far - the next three bytes + // would exceed the length, and the CBOR spec forbids + // splitting characters when generating text strings + if (!streaming) { + stream.WriteByte((byte)0x7f); + } + WritePositiveInt(3, byteIndex, stream); + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + streaming = true; + } + bytes[byteIndex++] = (byte)(0xe0 | ((c >> 12) & 0x0f)); + bytes[byteIndex++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + bytes[byteIndex++] = (byte)(0x80 | (c & 0x3f)); + } else { + if (byteIndex + 4 > StreamedStringBufferLength) { + // Write bytes retrieved so far - the next four bytes + // would exceed the length, and the CBOR spec forbids + // splitting characters when generating text strings + if (!streaming) { + stream.WriteByte((byte)0x7f); + } + WritePositiveInt(3, byteIndex, stream); + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + streaming = true; + } + bytes[byteIndex++] = (byte)(0xf0 | ((c >> 18) & 0x07)); + bytes[byteIndex++] = (byte)(0x80 | ((c >> 12) & 0x3f)); + bytes[byteIndex++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + bytes[byteIndex++] = (byte)(0x80 | (c & 0x3f)); + } + } + } + WritePositiveInt(3, byteIndex, stream); + stream.Write(bytes, 0, byteIndex); + if (streaming) { + stream.WriteByte((byte)0xff); + } + } + + //----------------------------------------------------------- + private void AppendClosingTags(StringBuilder sb) { + CBORObject curobject = this; + while (curobject.IsTagged) { + sb.Append(')'); + curobject = (CBORObject)curobject.itemValue; + } + } + + private void AppendOpeningTags(StringBuilder sb) { + CBORObject curobject = this; + while (curobject.IsTagged) { + int low = curobject.tagLow; + int high = curobject.tagHigh; + if (high == 0 && (low >> 16) == 0) { + sb.Append(CBORUtilities.LongToString(low)); + } else { + EInteger bi = LowHighToEInteger(low, high); + sb.Append(bi.ToString()); + } + sb.Append('('); + curobject = (CBORObject)curobject.itemValue; + } + } + + private int AsInt32(int minValue, int maxValue) { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("not a number type"); + } + return cn.AsInt32(this.ThisItem, minValue, maxValue); + } + + private void WriteTags(Stream s) { + CBORObject curobject = this; + while (curobject.IsTagged) { + int low = curobject.tagLow; + int high = curobject.tagHigh; + if (high == 0 && (low >> 16) == 0) { + WritePositiveInt(6, low, s); + } else if (high == 0) { + long value = ((long)low) & 0xffffffffL; + WritePositiveInt64(6, value, s); + } else if ((high >> 16) == 0) { + long value = ((long)low) & 0xffffffffL; + long highValue = ((long)high) & 0xffffffffL; + value |= highValue << 32; + WritePositiveInt64(6, value, s); + } else { + byte[] arrayToWrite = { (byte)0xdb, + (byte)((high >> 24) & 0xff), (byte)((high >> 16) & 0xff), + (byte)((high >> 8) & 0xff), (byte)(high & 0xff), + (byte)((low >> 24) & 0xff), (byte)((low >> 16) & 0xff), + (byte)((low >> 8) & 0xff), (byte)(low & 0xff) }; + s.Write(arrayToWrite, 0, 9); + } + curobject = (CBORObject)curobject.itemValue; + } + } + + private sealed class ConverterInfo { + private object toObject; + + /// + public object ToObject { + get { + return this.toObject; + } + + set { + this.toObject = value; + } + } + + private object converter; + + /// + public object Converter { + get { + return this.converter; + } + + set { + this.converter = value; + } + } + } + } +} diff --git a/PeterO/Cbor/CBORObjectExtra.cs b/PeterO/Cbor/CBORObjectExtra.cs new file mode 100644 index 00000000..a7f68a99 --- /dev/null +++ b/PeterO/Cbor/CBORObjectExtra.cs @@ -0,0 +1,291 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.IO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + // Contains extra methods placed separately + // because they are not CLS-compliant or they + // are specific to the .NET framework. + public sealed partial class CBORObject { + /// + [CLSCompliant(false)] + public ushort AsUInt16() { + int v = this.AsInt32(); + if (v > UInt16.MaxValue || v < 0) { + throw new OverflowException("This object's value is out of range"); + } + return (ushort)v; + } + + /// + [CLSCompliant(false)] + public uint AsUInt32() { + ulong v = this.AsUInt64(); + if (v > UInt32.MaxValue) { + throw new OverflowException("This object's value is out of range"); + } + return (uint)v; + } + + /// + [CLSCompliant(false)] + public sbyte AsSByte() { + int v = this.AsInt32(); + if (v > SByte.MaxValue || v < SByte.MinValue) { + throw new OverflowException("This object's value is out of range"); + } + return (sbyte)v; + } + + /// + [CLSCompliant(false)] + public static int WriteValue( + Stream outputStream, + int majorType, + uint value) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); +} + return WriteValue(outputStream, majorType, (long)value); + } + + /// + [CLSCompliant(false)] + public static int WriteValue( + Stream outputStream, + int majorType, + ulong value) { + if (outputStream == null) { + throw new ArgumentNullException(nameof(outputStream)); +} + if (value <= Int64.MaxValue) { + return WriteValue(outputStream, majorType, (long)value); + } else { + if (majorType < 0) { + throw new ArgumentException("majorType (" + majorType + + ") is less than 0"); +} +if (majorType > 7) { + throw new ArgumentException("majorType (" + majorType + + ") is more than 7"); +} + if (majorType == 7) { + throw new ArgumentException("majorType is 7 and value is greater than 255"); + } + byte[] bytes = { (byte)(27 | (majorType << 5)), (byte)((value >> + 56) & 0xff), + (byte)((value >> 48) & 0xff), (byte)((value >> 40) & 0xff), + (byte)((value >> 32) & 0xff), (byte)((value >> 24) & 0xff), + (byte)((value >> 16) & 0xff), (byte)((value >> 8) & 0xff), + (byte)(value & 0xff) }; + outputStream.Write(bytes, 0, bytes.Length); + return bytes.Length; + } + } + + private static EInteger DecimalToEInteger(decimal dec) { + return ((EDecimal)dec).ToEInteger(); + } + + private static decimal ExtendedRationalToDecimal(ERational + extendedNumber) { + return (decimal)extendedNumber; + } + + private static decimal ExtendedDecimalToDecimal(EDecimal + extendedNumber) { + return (decimal)extendedNumber; + } + + /// + [CLSCompliant(false)] + public decimal AsDecimal() { + return (this.ItemType == CBORObjectTypeInteger) ? + ((decimal)(long)this.ThisItem) : ((this.ItemType == + CBORObjectTypeExtendedRational) ? + ExtendedRationalToDecimal((ERational)this.ThisItem) : + ExtendedDecimalToDecimal(this.AsEDecimal())); + } + + /// + [CLSCompliant(false)] + public ulong AsUInt64() { + ICBORNumber cn = NumberInterfaces[this.ItemType]; + if (cn == null) { + throw new InvalidOperationException("Not a number type"); + } + EInteger bigint = cn.AsEInteger(this.ThisItem); + if (bigint.Sign < 0 || bigint.GetSignedBitLength() > 64) { + throw new OverflowException("This object's value is out of range"); + } + return (ulong)bigint; + } + + /// + [CLSCompliant(false)] + public static void Write(sbyte value, Stream stream) { + Write((long)value, stream); + } + + /// + [CLSCompliant(false)] + public static void Write(ulong value, Stream stream) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (value <= Int64.MaxValue) { + Write((long)value, stream); + } else { + stream.WriteByte((byte)27); + stream.WriteByte((byte)((value >> 56) & 0xff)); + stream.WriteByte((byte)((value >> 48) & 0xff)); + stream.WriteByte((byte)((value >> 40) & 0xff)); + stream.WriteByte((byte)((value >> 32) & 0xff)); + stream.WriteByte((byte)((value >> 24) & 0xff)); + stream.WriteByte((byte)((value >> 16) & 0xff)); + stream.WriteByte((byte)((value >> 8) & 0xff)); + stream.WriteByte((byte)(value & 0xff)); + } + } + + /// + public static CBORObject FromObject(decimal value) { + return FromObject((EDecimal)value); + } + + /// + [CLSCompliant(false)] + public static void Write(uint value, Stream stream) { + Write((ulong)value, stream); + } + + /// + [CLSCompliant(false)] + public static void Write(ushort value, Stream stream) { + Write((ulong)value, stream); + } + + /// + [CLSCompliant(false)] + public static CBORObject FromObject(sbyte value) { + return FromObject((long)value); + } + + private static EInteger UInt64ToEInteger(ulong value) { + var data = new byte[9]; + ulong uvalue = value; + data[0] = (byte)(uvalue & 0xff); + data[1] = (byte)((uvalue >> 8) & 0xff); + data[2] = (byte)((uvalue >> 16) & 0xff); + data[3] = (byte)((uvalue >> 24) & 0xff); + data[4] = (byte)((uvalue >> 32) & 0xff); + data[5] = (byte)((uvalue >> 40) & 0xff); + data[6] = (byte)((uvalue >> 48) & 0xff); + data[7] = (byte)((uvalue >> 56) & 0xff); + data[8] = (byte)0; + return EInteger.FromBytes(data, true); + } + + /// + [CLSCompliant(false)] + public static CBORObject FromObject(ulong value) { + return CBORObject.FromObject(UInt64ToEInteger(value)); + } + + /// + [CLSCompliant(false)] + public static CBORObject FromObject(uint value) { + return FromObject((long)(Int64)value); + } + + /// + [CLSCompliant(false)] + public static CBORObject FromObject(ushort value) { + return FromObject((long)(Int64)value); + } + + /// + [CLSCompliant(false)] + public static CBORObject FromObjectAndTag(Object o, ulong tag) { + return FromObjectAndTag(o, UInt64ToEInteger(tag)); + } + + /// + public T ToObject() { + return (T)this.ToObject(typeof(T)); + } + + /// + public T ToObject(CBORTypeMapper mapper) { + return (T)this.ToObject(typeof(T), mapper); + } + + /// + public T ToObject(PODOptions options) { + return (T)this.ToObject(typeof(T), options); + } + + /// + public T ToObject(CBORTypeMapper mapper, PODOptions options) { + return (T)this.ToObject(typeof(T), mapper, options); + } + + /// + public static CBORObject operator +(CBORObject a, CBORObject b) { + return Addition(a, b); + } + + /// + public static CBORObject operator -(CBORObject a, CBORObject b) { + return Subtract(a, b); + } + + /// + public static CBORObject operator *(CBORObject a, CBORObject b) { + return Multiply(a, b); + } + + /// + public static CBORObject operator /(CBORObject a, CBORObject b) { + return Divide(a, b); + } + + /// + public static CBORObject operator %(CBORObject a, CBORObject b) { + return Remainder(a, b); + } + } +} diff --git a/PeterO/Cbor/CBORObjectFactory.cs b/PeterO/Cbor/CBORObjectFactory.cs new file mode 100644 index 00000000..5ba14825 --- /dev/null +++ b/PeterO/Cbor/CBORObjectFactory.cs @@ -0,0 +1,8 @@ +using System; + +namespace PeterO.Cbor { + internal class CBORObjectFactory { + public CBORObjectFactory() { + } + } +} diff --git a/PeterO/Cbor/CBORObjectMath.cs b/PeterO/Cbor/CBORObjectMath.cs new file mode 100644 index 00000000..57d20dfd --- /dev/null +++ b/PeterO/Cbor/CBORObjectMath.cs @@ -0,0 +1,382 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + /// + internal static class CBORObjectMath { + public static CBORObject Addition(CBORObject a, CBORObject b) { + if (a == null) { + throw new ArgumentNullException(nameof(a)); + } + if (b == null) { + throw new ArgumentNullException(nameof(b)); + } + if (a.Type != CBORType.Number) { + throw new ArgumentException("a.Type (" + a.Type + + ") is not equal to " + CBORType.Number); + } + if (b.Type != CBORType.Number) { + throw new ArgumentException("b.Type (" + b.Type + + ") is not equal to " + CBORType.Number); + } + object objA = a.ThisItem; + object objB = b.ThisItem; + int typeA = a.ItemType; + int typeB = b.ItemType; + if (typeA == CBORObject.CBORObjectTypeInteger && typeB == + CBORObject.CBORObjectTypeInteger) { + var valueA = (long)objA; + var valueB = (long)objB; + if ((valueA < 0 && valueB < Int64.MinValue - valueA) || + (valueA > 0 && valueB > Int64.MaxValue - valueA)) { + // would overflow, convert to EInteger + return CBORObject.FromObject(((EInteger)valueA) + + (EInteger)valueB); + } + return CBORObject.FromObject(valueA + valueB); + } + if (typeA == CBORObject.CBORObjectTypeExtendedRational || + typeB == CBORObject.CBORObjectTypeExtendedRational) { + ERational e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(e1.Add(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedDecimal || + typeB == CBORObject.CBORObjectTypeExtendedDecimal) { + EDecimal e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedDecimal(objA); + EDecimal e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedDecimal(objB); + return CBORObject.FromObject(e1.Add(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedFloat || typeB == + CBORObject.CBORObjectTypeExtendedFloat || + typeA == CBORObject.CBORObjectTypeDouble || typeB == + CBORObject.CBORObjectTypeDouble || + typeA == CBORObject.CBORObjectTypeSingle || typeB == + CBORObject.CBORObjectTypeSingle) { + EFloat e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedFloat(objA); + EFloat e2 = CBORObject.GetNumberInterface(typeB).AsExtendedFloat(objB); + return CBORObject.FromObject(e1.Add(e2)); + } else { + EInteger b1 = CBORObject.GetNumberInterface(typeA).AsEInteger(objA); + EInteger b2 = CBORObject.GetNumberInterface(typeB).AsEInteger(objB); + return CBORObject.FromObject(b1 + (EInteger)b2); + } + } + + public static CBORObject Subtract(CBORObject a, CBORObject b) { + if (a == null) { + throw new ArgumentNullException(nameof(a)); + } + if (b == null) { + throw new ArgumentNullException(nameof(b)); + } + if (a.Type != CBORType.Number) { + throw new ArgumentException("a.Type (" + a.Type + + ") is not equal to " + CBORType.Number); + } + if (b.Type != CBORType.Number) { + throw new ArgumentException("b.Type (" + b.Type + + ") is not equal to " + CBORType.Number); + } + object objA = a.ThisItem; + object objB = b.ThisItem; + int typeA = a.ItemType; + int typeB = b.ItemType; + if (typeA == CBORObject.CBORObjectTypeInteger && typeB == + CBORObject.CBORObjectTypeInteger) { + var valueA = (long)objA; + var valueB = (long)objB; + if ((valueB < 0 && Int64.MaxValue + valueB < valueA) || + (valueB > 0 && Int64.MinValue + valueB > valueA)) { + // would overflow, convert to EInteger + return CBORObject.FromObject(((EInteger)valueA) - + (EInteger)valueB); + } + return CBORObject.FromObject(valueA - valueB); + } + if (typeA == CBORObject.CBORObjectTypeExtendedRational || + typeB == CBORObject.CBORObjectTypeExtendedRational) { + ERational e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(e1.Subtract(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedDecimal || + typeB == CBORObject.CBORObjectTypeExtendedDecimal) { + EDecimal e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedDecimal(objA); + EDecimal e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedDecimal(objB); + return CBORObject.FromObject(e1.Subtract(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedFloat || typeB == + CBORObject.CBORObjectTypeExtendedFloat || + typeA == CBORObject.CBORObjectTypeDouble || typeB == + CBORObject.CBORObjectTypeDouble || + typeA == CBORObject.CBORObjectTypeSingle || typeB == + CBORObject.CBORObjectTypeSingle) { + EFloat e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedFloat(objA); + EFloat e2 = CBORObject.GetNumberInterface(typeB).AsExtendedFloat(objB); + return CBORObject.FromObject(e1.Subtract(e2)); + } else { + EInteger b1 = CBORObject.GetNumberInterface(typeA).AsEInteger(objA); + EInteger b2 = CBORObject.GetNumberInterface(typeB).AsEInteger(objB); + return CBORObject.FromObject(b1 - (EInteger)b2); + } + } + + public static CBORObject Multiply(CBORObject a, CBORObject b) { + if (a == null) { + throw new ArgumentNullException(nameof(a)); + } + if (b == null) { + throw new ArgumentNullException(nameof(b)); + } + if (a.Type != CBORType.Number) { + throw new ArgumentException("a.Type (" + a.Type + + ") is not equal to " + CBORType.Number); + } + if (b.Type != CBORType.Number) { + throw new ArgumentException("b.Type (" + b.Type + + ") is not equal to " + CBORType.Number); + } + object objA = a.ThisItem; + object objB = b.ThisItem; + int typeA = a.ItemType; + int typeB = b.ItemType; + if (typeA == CBORObject.CBORObjectTypeInteger && typeB == + CBORObject.CBORObjectTypeInteger) { + var valueA = (long)objA; + var valueB = (long)objB; + bool apos = valueA > 0L; + bool bpos = valueB > 0L; + if ( + (apos && ((!bpos && (Int64.MinValue / valueA) > valueB) || + (bpos && valueA > (Int64.MaxValue / valueB)))) || + (!apos && ((!bpos && valueA != 0L && + (Int64.MaxValue / valueA) > valueB) || + (bpos && valueA < (Int64.MinValue / valueB))))) { + // would overflow, convert to EInteger + var bvalueA = (EInteger)valueA; + var bvalueB = (EInteger)valueB; + return CBORObject.FromObject(bvalueA * (EInteger)bvalueB); + } + return CBORObject.FromObject(valueA * valueB); + } + if (typeA == CBORObject.CBORObjectTypeExtendedRational || + typeB == CBORObject.CBORObjectTypeExtendedRational) { + ERational e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(e1.Multiply(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedDecimal || + typeB == CBORObject.CBORObjectTypeExtendedDecimal) { + EDecimal e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedDecimal(objA); + EDecimal e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedDecimal(objB); + return CBORObject.FromObject(e1.Multiply(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedFloat || typeB == + CBORObject.CBORObjectTypeExtendedFloat || + typeA == CBORObject.CBORObjectTypeDouble || typeB == + CBORObject.CBORObjectTypeDouble || + typeA == CBORObject.CBORObjectTypeSingle || typeB == + CBORObject.CBORObjectTypeSingle) { + EFloat e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedFloat(objA); + EFloat e2 = CBORObject.GetNumberInterface(typeB).AsExtendedFloat(objB); + return CBORObject.FromObject(e1.Multiply(e2)); + } else { + EInteger b1 = CBORObject.GetNumberInterface(typeA).AsEInteger(objA); + EInteger b2 = CBORObject.GetNumberInterface(typeB).AsEInteger(objB); + return CBORObject.FromObject(b1 * (EInteger)b2); + } + } + + public static CBORObject Divide(CBORObject a, CBORObject b) { + if (a == null) { + throw new ArgumentNullException(nameof(a)); + } + if (b == null) { + throw new ArgumentNullException(nameof(b)); + } + if (a.Type != CBORType.Number) { + throw new ArgumentException("a.Type (" + a.Type + + ") is not equal to " + CBORType.Number); + } + if (b.Type != CBORType.Number) { + throw new ArgumentException("b.Type (" + b.Type + + ") is not equal to " + CBORType.Number); + } + object objA = a.ThisItem; + object objB = b.ThisItem; + int typeA = a.ItemType; + int typeB = b.ItemType; + if (typeA == CBORObject.CBORObjectTypeInteger && typeB == + CBORObject.CBORObjectTypeInteger) { + var valueA = (long)objA; + var valueB = (long)objB; + if (valueB == 0) { + return (valueA == 0) ? CBORObject.NaN : ((valueA < 0) ? + CBORObject.NegativeInfinity : CBORObject.PositiveInfinity); + } + if (valueA == Int64.MinValue && valueB == -1) { + return CBORObject.FromObject(valueA).Negate(); + } + long quo = valueA / valueB; + long rem = valueA - (quo * valueB); + return (rem == 0) ? CBORObject.FromObject(quo) : + CBORObject.FromObject( + ERational.Create( + (EInteger)valueA, + (EInteger)valueB)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedRational || + typeB == CBORObject.CBORObjectTypeExtendedRational) { + ERational e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(e1.Divide(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedDecimal || + typeB == CBORObject.CBORObjectTypeExtendedDecimal) { + EDecimal e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedDecimal(objA); + EDecimal e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedDecimal(objB); + if (e1.IsZero && e2.IsZero) { + return CBORObject.NaN; + } + EDecimal eret = e1.Divide(e2, null); + // If either operand is infinity or NaN, the result + // is already exact. Likewise if the result is a finite number. + if (!e1.IsFinite || !e2.IsFinite || eret.IsFinite) { + return CBORObject.FromObject(eret); + } + ERational er1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational er2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(er1.Divide(er2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedFloat || typeB == + CBORObject.CBORObjectTypeExtendedFloat || + typeA == CBORObject.CBORObjectTypeDouble || typeB == + CBORObject.CBORObjectTypeDouble || + typeA == CBORObject.CBORObjectTypeSingle || typeB == + CBORObject.CBORObjectTypeSingle) { + EFloat e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedFloat(objA); + EFloat e2 = CBORObject.GetNumberInterface(typeB).AsExtendedFloat(objB); + if (e1.IsZero && e2.IsZero) { + return CBORObject.NaN; + } + EFloat eret = e1.Divide(e2, null); + // If either operand is infinity or NaN, the result + // is already exact. Likewise if the result is a finite number. + if (!e1.IsFinite || !e2.IsFinite || eret.IsFinite) { + return CBORObject.FromObject(eret); + } + ERational er1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational er2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(er1.Divide(er2)); + } else { + EInteger b1 = CBORObject.GetNumberInterface(typeA).AsEInteger(objA); + EInteger b2 = CBORObject.GetNumberInterface(typeB).AsEInteger(objB); + if (b2.IsZero) { + return b1.IsZero ? CBORObject.NaN : ((b1.Sign < 0) ? + CBORObject.NegativeInfinity : CBORObject.PositiveInfinity); + } + EInteger bigrem; + EInteger bigquo; +{ +EInteger[] divrem = b1.DivRem(b2); +bigquo = divrem[0]; +bigrem = divrem[1]; } + return bigrem.IsZero ? CBORObject.FromObject(bigquo) : + CBORObject.FromObject(ERational.Create(b1, b2)); + } + } + + public static CBORObject Remainder(CBORObject a, CBORObject b) { + if (a == null) { + throw new ArgumentNullException(nameof(a)); + } + if (b == null) { + throw new ArgumentNullException(nameof(b)); + } + if (a.Type != CBORType.Number) { + throw new ArgumentException("a.Type (" + a.Type + + ") is not equal to " + CBORType.Number); + } + if (b.Type != CBORType.Number) { + throw new ArgumentException("b.Type (" + b.Type + + ") is not equal to " + CBORType.Number); + } + object objA = a.ThisItem; + object objB = b.ThisItem; + int typeA = a.ItemType; + int typeB = b.ItemType; + if (typeA == CBORObject.CBORObjectTypeInteger && typeB == + CBORObject.CBORObjectTypeInteger) { + var valueA = (long)objA; + var valueB = (long)objB; + return (valueA == Int64.MinValue && valueB == -1) ? + CBORObject.FromObject(0) : CBORObject.FromObject(valueA % valueB); + } + if (typeA == CBORObject.CBORObjectTypeExtendedRational || + typeB == CBORObject.CBORObjectTypeExtendedRational) { + ERational e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedRational(objA); + ERational e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedRational(objB); + return CBORObject.FromObject(e1.Remainder(e2)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedDecimal || + typeB == CBORObject.CBORObjectTypeExtendedDecimal) { + EDecimal e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedDecimal(objA); + EDecimal e2 = + CBORObject.GetNumberInterface(typeB).AsExtendedDecimal(objB); + return CBORObject.FromObject(e1.Remainder(e2, null)); + } + if (typeA == CBORObject.CBORObjectTypeExtendedFloat || typeB == + CBORObject.CBORObjectTypeExtendedFloat || + typeA == CBORObject.CBORObjectTypeDouble || typeB == + CBORObject.CBORObjectTypeDouble || + typeA == CBORObject.CBORObjectTypeSingle || typeB == + CBORObject.CBORObjectTypeSingle) { + EFloat e1 = + CBORObject.GetNumberInterface(typeA).AsExtendedFloat(objA); + EFloat e2 = CBORObject.GetNumberInterface(typeB).AsExtendedFloat(objB); + return CBORObject.FromObject(e1.Remainder(e2, null)); + } else { + EInteger b1 = CBORObject.GetNumberInterface(typeA).AsEInteger(objA); + EInteger b2 = CBORObject.GetNumberInterface(typeB).AsEInteger(objB); + return CBORObject.FromObject(b1 % (EInteger)b2); + } + } + } +} diff --git a/PeterO/Cbor/CBORReader.cs b/PeterO/Cbor/CBORReader.cs new file mode 100644 index 00000000..a356c84b --- /dev/null +++ b/PeterO/Cbor/CBORReader.cs @@ -0,0 +1,634 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.IO; +using System.Text; +using PeterO; +using PeterO.Numbers; +#pragma warning disable 618 + +namespace PeterO.Cbor { + internal class CBORReader { + private readonly Stream stream; + private int depth; + private CBORDuplicatePolicy policy; + private StringRefs stringRefs; + private bool hasSharableObjects; + + public CBORReader(Stream inStream) { + this.stream = inStream; + this.policy = CBORDuplicatePolicy.Overwrite; + } + + internal enum CBORDuplicatePolicy { + Overwrite, Disallow + } + + public CBORDuplicatePolicy DuplicatePolicy { + get { + return this.policy; + } + + set { + this.policy = value; + } + } + + public CBORObject ResolveSharedRefsIfNeeded(CBORObject obj) { + if (this.hasSharableObjects) { + var sharedRefs = new SharedRefs(); + return ResolveSharedRefs(obj, sharedRefs); + } + return obj; + } + + private static CBORObject ResolveSharedRefs( + CBORObject obj, + SharedRefs sharedRefs) { + int type = obj.ItemType; + bool hasTag = obj.MostOuterTag.Equals((EInteger)29); + if (hasTag) { + return sharedRefs.GetObject(obj.AsEInteger()); + } + hasTag = obj.MostOuterTag.Equals((EInteger)28); + if (hasTag) { + obj = obj.Untag(); + sharedRefs.AddObject(obj); + } + if (type == CBORObject.CBORObjectTypeMap) { + foreach (CBORObject key in obj.Keys) { + CBORObject value = obj[key]; + CBORObject newvalue = ResolveSharedRefs(value, sharedRefs); + if (value != newvalue) { + obj[key] = newvalue; + } + } + } else if (type == CBORObject.CBORObjectTypeArray) { + for (var i = 0; i < obj.Count; ++i) { + obj[i] = ResolveSharedRefs(obj[i], sharedRefs); + } + } + return obj; + } + + public CBORObject Read(CBORTypeFilter filter) { + if (this.depth > 500) { + throw new CBORException("Too deeply nested"); + } + int firstbyte = this.stream.ReadByte(); + if (firstbyte < 0) { + throw new CBORException("Premature end of data"); + } + return this.ReadForFirstByte(firstbyte, filter); + } + + public CBORObject ReadForFirstByte( + int firstbyte, + CBORTypeFilter filter) { + if (this.depth > 500) { + throw new CBORException("Too deeply nested"); + } + if (firstbyte < 0) { + throw new CBORException("Premature end of data"); + } + if (firstbyte == 0xff) { + throw new CBORException("Unexpected break code encountered"); + } + int type = (firstbyte >> 5) & 0x07; + int additional = firstbyte & 0x1f; + int expectedLength = CBORObject.GetExpectedLength(firstbyte); + // Data checks + if (expectedLength == -1) { + // if the head byte is invalid + throw new CBORException("Unexpected data encountered"); + } + if (filter != null) { + // Check for valid major types if asked + if (!filter.MajorTypeMatches(type)) { + throw new CBORException("Unexpected data type encountered"); + } + if (firstbyte >= 0xe0 && firstbyte <= 0xff && firstbyte != 0xf9 && + firstbyte != 0xfa && firstbyte != 0xfb) { + if (!filter.NonFPSimpleValueAllowed()) { + throw new CBORException("Unexpected data type encountered"); + } + } + } + // Check if this represents a fixed object + CBORObject fixedObject = CBORObject.GetFixedObject(firstbyte); + if (fixedObject != null) { + return fixedObject; + } + // Read fixed-length data + byte[] data = null; + if (expectedLength != 0) { + data = new byte[expectedLength]; + // include the first byte because GetFixedLengthObject + // will assume it exists for some head bytes + data[0] = unchecked((byte)firstbyte); + if (expectedLength > 1 && + this.stream.Read(data, 1, expectedLength - 1) != expectedLength + - 1) { + throw new CBORException("Premature end of data"); + } + CBORObject cbor = CBORObject.GetFixedLengthObject(firstbyte, data); + if (this.stringRefs != null && (type == 2 || type == 3)) { + this.stringRefs.AddStringIfNeeded(cbor, expectedLength - 1); + } + return cbor; + } + var uadditional = (long)additional; + EInteger bigintAdditional = EInteger.Zero; + var hasBigAdditional = false; + data = new byte[8]; + var lowAdditional = 0; + switch (firstbyte & 0x1f) { + case 24: { + int tmp = this.stream.ReadByte(); + if (tmp < 0) { + throw new CBORException("Premature end of data"); + } + lowAdditional = tmp; + uadditional = lowAdditional; + break; + } + case 25: { + if (this.stream.Read(data, 0, 2) != 2) { + throw new CBORException("Premature end of data"); + } + lowAdditional = ((int)(data[0] & (int)0xff)) << 8; + lowAdditional |= (int)(data[1] & (int)0xff); + uadditional = lowAdditional; + break; + } + case 26: { + if (this.stream.Read(data, 0, 4) != 4) { + throw new CBORException("Premature end of data"); + } + uadditional = ((long)(data[0] & (long)0xff)) << 24; + uadditional |= ((long)(data[1] & (long)0xff)) << 16; + uadditional |= ((long)(data[2] & (long)0xff)) << 8; + uadditional |= (long)(data[3] & (long)0xff); + break; + } + case 27: { + if (this.stream.Read(data, 0, 8) != 8) { + throw new CBORException("Premature end of data"); + } + if ((((int)data[0]) & 0x80) != 0) { + // Won't fit in a signed 64-bit number + var uabytes = new byte[9]; + uabytes[0] = data[7]; + uabytes[1] = data[6]; + uabytes[2] = data[5]; + uabytes[3] = data[4]; + uabytes[4] = data[3]; + uabytes[5] = data[2]; + uabytes[6] = data[1]; + uabytes[7] = data[0]; + uabytes[8] = 0; + hasBigAdditional = true; + bigintAdditional = EInteger.FromBytes(uabytes, true); + } else { + uadditional = ((long)(data[0] & (long)0xff)) << 56; + uadditional |= ((long)(data[1] & (long)0xff)) << 48; + uadditional |= ((long)(data[2] & (long)0xff)) << 40; + uadditional |= ((long)(data[3] & (long)0xff)) << 32; + uadditional |= ((long)(data[4] & (long)0xff)) << 24; + uadditional |= ((long)(data[5] & (long)0xff)) << 16; + uadditional |= ((long)(data[6] & (long)0xff)) << 8; + uadditional |= (long)(data[7] & (long)0xff); + } + break; + } + } + // The following doesn't check for major types 0 and 1, + // since all of them are fixed-length types and are + // handled in the call to GetFixedLengthObject. + if (type == 2) { // Byte string + if (additional == 31) { + // Streaming byte string + using (var ms = new MemoryStream()) { + // Requires same type as this one + while (true) { + int nextByte = this.stream.ReadByte(); + if (nextByte == 0xff) { + // break if the "break" code was read + break; + } + long len = ReadDataLength(this.stream, nextByte, 2); + if ((len >> 63) != 0 || len > Int32.MaxValue) { + throw new CBORException("Length" + ToUnsignedBigInteger(len) + + " is bigger than supported "); + } + if (nextByte != 0x40) { + // NOTE: 0x40 means the empty byte string + ReadByteData(this.stream, len, ms); + } + } + if (ms.Position > Int32.MaxValue) { + throw new + CBORException("Length of bytes to be streamed is bigger than supported "); + } + data = ms.ToArray(); + return new CBORObject( + CBORObject.CBORObjectTypeByteString, + data); + } + } else { + if (hasBigAdditional) { + throw new CBORException("Length of " + + bigintAdditional.ToString() + " is bigger than supported"); + } + if (uadditional > Int32.MaxValue) { + throw new CBORException("Length of " + + CBORUtilities.LongToString(uadditional) + + " is bigger than supported"); + } + data = ReadByteData(this.stream, uadditional, null); + var cbor = new CBORObject(CBORObject.CBORObjectTypeByteString, data); + if (this.stringRefs != null) { + int hint = (uadditional > Int32.MaxValue || hasBigAdditional) ? + Int32.MaxValue : (int)uadditional; + this.stringRefs.AddStringIfNeeded(cbor, hint); + } + return cbor; + } + } + if (type == 3) { // Text string + if (additional == 31) { + // Streaming text string + var builder = new StringBuilder(); + while (true) { + int nextByte = this.stream.ReadByte(); + if (nextByte == 0xff) { + // break if the "break" code was read + break; + } + long len = ReadDataLength(this.stream, nextByte, 3); + if ((len >> 63) != 0 || len > Int32.MaxValue) { + throw new CBORException("Length" + ToUnsignedBigInteger(len) + + " is bigger than supported"); + } + if (nextByte != 0x60) { + // NOTE: 0x60 means the empty string + if (PropertyMap.ExceedsKnownLength(this.stream, len)) { + throw new CBORException("Premature end of data"); + } + switch ( + DataUtilities.ReadUtf8( + this.stream, + (int)len, + builder, + false)) { + case -1: + throw new CBORException("Invalid UTF-8"); + case -2: + throw new CBORException("Premature end of data"); + } + } + } + return new CBORObject( + CBORObject.CBORObjectTypeTextString, + builder.ToString()); + } else { + if (hasBigAdditional) { + throw new CBORException("Length of " + + bigintAdditional.ToString() + " is bigger than supported"); + } + if (uadditional > Int32.MaxValue) { + throw new CBORException("Length of " + + CBORUtilities.LongToString(uadditional) + + " is bigger than supported"); + } + if (PropertyMap.ExceedsKnownLength(this.stream, uadditional)) { + throw new CBORException("Premature end of data"); + } + var builder = new StringBuilder(); + switch ( + DataUtilities.ReadUtf8( + this.stream, + (int)uadditional, + builder, + false)) { + case -1: + throw new CBORException("Invalid UTF-8"); + case -2: + throw new CBORException("Premature end of data"); + } + var cbor = new CBORObject( + CBORObject.CBORObjectTypeTextString, + builder.ToString()); + if (this.stringRefs != null) { + int hint = (uadditional > Int32.MaxValue || hasBigAdditional) ? + Int32.MaxValue : (int)uadditional; + this.stringRefs.AddStringIfNeeded(cbor, hint); + } + return cbor; + } + } + if (type == 4) { // Array + CBORObject cbor = CBORObject.NewArray(); + if (additional == 31) { + var vtindex = 0; + // Indefinite-length array + while (true) { + int headByte = this.stream.ReadByte(); + if (headByte < 0) { + throw new CBORException("Premature end of data"); + } + if (headByte == 0xff) { + // Break code was read + break; + } + if (filter != null && !filter.ArrayIndexAllowed(vtindex)) { + throw new CBORException("Array is too long"); + } + ++this.depth; + CBORObject o = this.ReadForFirstByte( + headByte, + filter == null ? null : filter.GetSubFilter(vtindex)); + --this.depth; + cbor.Add(o); + ++vtindex; + } + return cbor; + } + if (hasBigAdditional) { + throw new CBORException("Length of " + + bigintAdditional.ToString() + " is bigger than supported"); + } + if (uadditional > Int32.MaxValue) { + throw new CBORException("Length of " + + CBORUtilities.LongToString(uadditional) + + " is bigger than supported"); + } + if (filter != null && !filter.ArrayLengthMatches(uadditional)) { + throw new CBORException("Array is too long"); + } + if (PropertyMap.ExceedsKnownLength(this.stream, uadditional)) { + throw new CBORException("Remaining data too small for array length"); + } + ++this.depth; + for (long i = 0; i < uadditional; ++i) { + cbor.Add( + this.Read(filter == null ? null : filter.GetSubFilter(i))); + } + --this.depth; + return cbor; + } + if (type == 5) { // Map, type 5 + CBORObject cbor = CBORObject.NewMap(); + if (additional == 31) { + // Indefinite-length map + while (true) { + int headByte = this.stream.ReadByte(); + if (headByte < 0) { + throw new CBORException("Premature end of data"); + } + if (headByte == 0xff) { + // Break code was read + break; + } + ++this.depth; + CBORObject key = this.ReadForFirstByte(headByte, null); + CBORObject value = this.Read(null); + --this.depth; + if (this.policy == CBORDuplicatePolicy.Disallow) { + if (cbor.ContainsKey(key)) { + throw new CBORException("Duplicate key already exists: " + key); + } + } + cbor[key] = value; + } + return cbor; + } + if (hasBigAdditional) { + throw new CBORException("Length of " + + bigintAdditional.ToString() + " is bigger than supported"); + } + if (uadditional > Int32.MaxValue) { + throw new CBORException("Length of " + + CBORUtilities.LongToString(uadditional) + + " is bigger than supported"); + } + if (PropertyMap.ExceedsKnownLength(this.stream, uadditional)) { + throw new CBORException("Remaining data too small for map length"); + } + for (long i = 0; i < uadditional; ++i) { + ++this.depth; + CBORObject key = this.Read(null); + CBORObject value = this.Read(null); + --this.depth; + if (this.policy == CBORDuplicatePolicy.Disallow) { + if (cbor.ContainsKey(key)) { + throw new CBORException("Duplicate key already exists: " + key); + } + } + cbor[key] = value; + } + return cbor; + } + if (type == 6) { // Tagged item + ICBORTag taginfo = null; + var haveFirstByte = false; + var newFirstByte = -1; + if (!hasBigAdditional) { + if (filter != null && !filter.TagAllowed(uadditional)) { + throw new CBORException("Unexpected tag encountered: " + + uadditional); + } + int uad = uadditional >= 257 ? 257 : (uadditional < 0 ? 0 : + (int)uadditional); + switch (uad) { + case 256: + // Tag 256: String namespace + this.stringRefs = this.stringRefs ?? (new StringRefs()); + this.stringRefs.Push(); + break; + case 25: + // String reference + if (this.stringRefs == null) { + throw new CBORException("No stringref namespace"); + } + break; + case 28: + case 29: + this.hasSharableObjects = true; + break; + } + + taginfo = CBORObject.FindTagConverterLong(uadditional); + } else { + if (filter != null && !filter.TagAllowed(bigintAdditional)) { + throw new CBORException("Unexpected tag encountered: " + + uadditional); + } + #pragma warning disable 618 + + taginfo = CBORObject.FindTagConverter(bigintAdditional); + } + ++this.depth; + CBORObject o = haveFirstByte ? this.ReadForFirstByte( + newFirstByte, + taginfo == null ? null : taginfo.GetTypeFilter()) : + this.Read(taginfo == null ? null : taginfo.GetTypeFilter()); + --this.depth; + if (hasBigAdditional) { + return CBORObject.FromObjectAndTag(o, bigintAdditional); + } + if (uadditional < 65536) { + int uaddl = uadditional >= 257 ? 257 : (uadditional < 0 ? 0 : + (int)uadditional); + switch (uaddl) { + case 256: + // string tag + this.stringRefs.Pop(); + break; + case 25: + // stringref tag + return this.stringRefs.GetString(o.AsEInteger()); + } + + return CBORObject.FromObjectAndTag( + o, + (int)uadditional); + } + return CBORObject.FromObjectAndTag( + o, + (EInteger)uadditional); + } + throw new CBORException("Unexpected data encountered"); + } + + private static byte[] ReadByteData( + Stream stream, + long uadditional, + Stream outputStream) { + if ((uadditional >> 63) != 0 || uadditional > Int32.MaxValue) { + throw new CBORException("Length" + ToUnsignedBigInteger(uadditional) + + " is bigger than supported "); + } + if (PropertyMap.ExceedsKnownLength(stream, uadditional)) { + throw new CBORException("Premature end of stream"); + } + if (uadditional <= 0x10000) { + // Simple case: small size + var data = new byte[(int)uadditional]; + if (stream.Read(data, 0, data.Length) != data.Length) { + throw new CBORException("Premature end of stream"); + } + if (outputStream != null) { + outputStream.Write(data, 0, data.Length); + return null; + } + return data; + } else { + var tmpdata = new byte[0x10000]; + var total = (int)uadditional; + if (outputStream != null) { + while (total > 0) { + int bufsize = Math.Min(tmpdata.Length, total); + if (stream.Read(tmpdata, 0, bufsize) != bufsize) { + throw new CBORException("Premature end of stream"); + } + outputStream.Write(tmpdata, 0, bufsize); + total -= bufsize; + } + return null; + } + using (var ms = new MemoryStream(0x10000)) { + while (total > 0) { + int bufsize = Math.Min(tmpdata.Length, total); + if (stream.Read(tmpdata, 0, bufsize) != bufsize) { + throw new CBORException("Premature end of stream"); + } + ms.Write(tmpdata, 0, bufsize); + total -= bufsize; + } + return ms.ToArray(); + } + } + } + + private static long ReadDataLength( + Stream stream, + int headByte, + int expectedType) { + if (headByte < 0) { + throw new CBORException("Unexpected data encountered"); + } + if (((headByte >> 5) & 0x07) != expectedType) { + throw new CBORException("Unexpected data encountered"); + } + headByte &= 0x1f; + if (headByte < 24) { + return headByte; + } + var data = new byte[8]; + switch (headByte & 0x1f) { + case 24: { + int tmp = stream.ReadByte(); + if (tmp < 0) { + throw new CBORException("Premature end of data"); + } + return tmp; + } + case 25: { + if (stream.Read(data, 0, 2) != 2) { + throw new CBORException("Premature end of data"); + } + int lowAdditional = ((int)(data[0] & (int)0xff)) << 8; + lowAdditional |= (int)(data[1] & (int)0xff); + return lowAdditional; + } + case 26: { + if (stream.Read(data, 0, 4) != 4) { + throw new CBORException("Premature end of data"); + } + long uadditional = ((long)(data[0] & (long)0xff)) << 24; + uadditional |= ((long)(data[1] & (long)0xff)) << 16; + uadditional |= ((long)(data[2] & (long)0xff)) << 8; + uadditional |= (long)(data[3] & (long)0xff); + return uadditional; + } + case 27: { + if (stream.Read(data, 0, 8) != 8) { + throw new CBORException("Premature end of data"); + } + // Treat return value as an unsigned integer + long uadditional = ((long)(data[0] & (long)0xff)) << 56; + uadditional |= ((long)(data[1] & (long)0xff)) << 48; + uadditional |= ((long)(data[2] & (long)0xff)) << 40; + uadditional |= ((long)(data[3] & (long)0xff)) << 32; + uadditional |= ((long)(data[4] & (long)0xff)) << 24; + uadditional |= ((long)(data[5] & (long)0xff)) << 16; + uadditional |= ((long)(data[6] & (long)0xff)) << 8; + uadditional |= (long)(data[7] & (long)0xff); + return uadditional; + } + case 28: + case 29: + case 30: + throw new CBORException("Unexpected data encountered"); + case 31: + throw new CBORException("Indefinite-length data not allowed here"); + default: return headByte; + } + } + + private static EInteger ToUnsignedBigInteger(long val) { + var lval = (EInteger)(val & ~(1L << 63)); + if ((val >> 63) != 0) { + EInteger bigintAdd = EInteger.One << 63; + lval += (EInteger)bigintAdd; + } + return lval; + } + } +} diff --git a/PeterO/Cbor/CBORSingle.cs b/PeterO/Cbor/CBORSingle.cs new file mode 100644 index 00000000..2ac74e85 --- /dev/null +++ b/PeterO/Cbor/CBORSingle.cs @@ -0,0 +1,162 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal sealed class CBORSingle : ICBORNumber + { + private const float SingleOneLsh64 = 9223372036854775808f; + + public bool IsPositiveInfinity(object obj) { + return Single.IsPositiveInfinity((float)obj); + } + + public bool IsInfinity(object obj) { + return Single.IsInfinity((float)obj); + } + + public bool IsNegativeInfinity(object obj) { + return Single.IsNegativeInfinity((float)obj); + } + + public bool IsNaN(object obj) { + return Single.IsNaN((float)obj); + } + + public double AsDouble(object obj) { + return (double)(float)obj; + } + + public EDecimal AsExtendedDecimal(object obj) { + return EDecimal.FromSingle((float)obj); + } + + public EFloat AsExtendedFloat(object obj) { + return EFloat.FromSingle((float)obj); + } + + public float AsSingle(object obj) { + return (float)obj; + } + + public EInteger AsEInteger(object obj) { + return CBORUtilities.BigIntegerFromSingle((float)obj); + } + + public long AsInt64(object obj) { + var fltItem = (float)obj; + if (Single.IsNaN(fltItem)) { + throw new OverflowException("This object's value is out of range"); + } + fltItem = (fltItem < 0) ? (float)Math.Ceiling(fltItem) : + (float)Math.Floor(fltItem); + if (fltItem >= -SingleOneLsh64 && fltItem < SingleOneLsh64) { + return (long)fltItem; + } + throw new OverflowException("This object's value is out of range"); + } + + public bool CanFitInSingle(object obj) { + return true; + } + + public bool CanFitInDouble(object obj) { + return true; + } + + public bool CanFitInInt32(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt32(obj); + } + + public bool CanFitInInt64(object obj) { + return this.IsIntegral(obj) && this.CanTruncatedIntFitInInt64(obj); + } + + public bool CanTruncatedIntFitInInt64(object obj) { + var fltItem = (float)obj; + if (Single.IsNaN(fltItem) || Single.IsInfinity(fltItem)) { + return false; + } + float fltItem2 = (fltItem < 0) ? (float)Math.Ceiling(fltItem) : + (float)Math.Floor(fltItem); + return fltItem2 >= -SingleOneLsh64 && fltItem2 < + SingleOneLsh64; + } + + public bool CanTruncatedIntFitInInt32(object obj) { + var fltItem = (float)obj; + if (Single.IsNaN(fltItem) || Single.IsInfinity(fltItem)) { + return false; + } + float fltItem2 = (fltItem < 0) ? (float)Math.Ceiling(fltItem) : + (float)Math.Floor(fltItem); + // Convert float to double to avoid precision loss when + // converting Int32.MinValue/MaxValue to float + return (double)fltItem2 >= Int32.MinValue && (double)fltItem2 <= + Int32.MaxValue; + } + + public int AsInt32(object obj, int minValue, int maxValue) { + var fltItem = (float)obj; + if (Single.IsNaN(fltItem)) { + throw new OverflowException("This object's value is out of range"); + } + fltItem = (fltItem < 0) ? (float)Math.Ceiling(fltItem) : + (float)Math.Floor(fltItem); + // Convert float to double to avoid precision loss when + // converting Int32.MinValue/MaxValue to float + if ((double)fltItem >= Int32.MinValue && (double)fltItem <= + Int32.MaxValue) { + var ret = (int)fltItem; + return ret; + } + throw new OverflowException("This object's value is out of range"); + } + + public bool IsZero(object obj) { + return (float)obj == 0.0f; + } + + public int Sign(object obj) { + var flt = (float)obj; + return Single.IsNaN(flt) ? 2 : (flt == 0.0f ? 0 : (flt < 0.0f ? -1 : 1)); + } + + public bool IsIntegral(object obj) { + var fltItem = (float)obj; + if (Single.IsNaN(fltItem) || Single.IsInfinity(fltItem)) { + return false; + } + float fltItem2 = (fltItem < 0) ? (float)Math.Ceiling(fltItem) : + (float)Math.Floor(fltItem); + return fltItem == fltItem2; + } + + public object Negate(object obj) { + var val = (float)obj; + return -val; + } + + public object Abs(object obj) { + var val = (float)obj; + return (val < 0) ? -val : obj; + } + + public ERational AsExtendedRational(object obj) { + return ERational.FromSingle((float)obj); + } + + public bool IsNegative(object obj) { + var val = (float)obj; + int ivalue = BitConverter.ToInt32(BitConverter.GetBytes((float)val), 0); + return (ivalue >> 31) != 0; + } + } +} diff --git a/PeterO/Cbor/CBORTag0.cs b/PeterO/Cbor/CBORTag0.cs new file mode 100644 index 00000000..6be2fdc0 --- /dev/null +++ b/PeterO/Cbor/CBORTag0.cs @@ -0,0 +1,73 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTag0 : ICBORTag, ICBORToFromConverter { + private static string DateTimeToString(DateTime bi) { + var lesserFields = new int[7]; + var year = new EInteger[1]; + PropertyMap.BreakDownDateTime(bi, year, lesserFields); + // TODO: Change to true in next major version + return CBORUtilities.ToAtomDateTimeString(year[0], lesserFields, false); + } + + internal static void AddConverter() { + // TODO: FromObject with Dates has different behavior + // in Java version, which has to be retained until + // the next major version for backward compatibility. + // However, since ToObject is new, we can convert + // to Date in the .NET and Java versions + if (PropertyMap.DateTimeCompatHack) { + CBORObject.AddConverter(typeof(DateTime), new CBORTag0()); + } + } + + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.TextString; + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.TextString) { + throw new CBORException("Not a text string"); + } + return obj; + } + + public DateTime FromCBORObject(CBORObject obj) { + if (obj.HasMostOuterTag(0)) { + return StringToDateTime(obj.AsString()); + } else if (obj.HasMostOuterTag(1)) { + if (!obj.IsFinite) { + throw new CBORException("Not a finite number"); + } + EDecimal dec = obj.AsEDecimal(); + var lesserFields = new int[7]; + var year = new EInteger[1]; + CBORUtilities.BreakDownSecondsSinceEpoch( + dec, + year, + lesserFields); + return PropertyMap.BuildUpDateTime(year[0], lesserFields); + } + throw new CBORException("Not tag 0 or 1"); + } + + public static DateTime StringToDateTime(string str) { + var lesserFields = new int[7]; + var year = new EInteger[1]; + CBORUtilities.ParseAtomDateTimeString(str, year, lesserFields); + return PropertyMap.BuildUpDateTime(year[0], lesserFields); + } + + public CBORObject ToCBORObject(DateTime obj) { + return CBORObject.FromObjectAndTag(DateTimeToString(obj), 0); + } + } +} diff --git a/PeterO/Cbor/CBORTag1.cs b/PeterO/Cbor/CBORTag1.cs new file mode 100644 index 00000000..cfe8a0cd --- /dev/null +++ b/PeterO/Cbor/CBORTag1.cs @@ -0,0 +1,31 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTag1 : ICBORTag, ICBORConverter + { + public CBORTypeFilter GetTypeFilter() { + return + CBORTypeFilter.UnsignedInteger.WithNegativeInteger().WithFloatingPoint(); + } + + public CBORObject ValidateObject(CBORObject obj) { + if (!obj.IsFinite) { + throw new CBORException("Not a valid date"); + } + return obj; + } + + public CBORObject ToCBORObject(DateTime obj) { + // TODO + throw new NotImplementedException(); + } + } +} diff --git a/PeterO/Cbor/CBORTag2.cs b/PeterO/Cbor/CBORTag2.cs new file mode 100644 index 00000000..9be060e7 --- /dev/null +++ b/PeterO/Cbor/CBORTag2.cs @@ -0,0 +1,91 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; +#pragma warning disable 618 + +namespace PeterO.Cbor { + internal class CBORTag2 : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.ByteString; + } + + private static CBORObject FromObjectAndInnerTags( + object objectValue, + CBORObject objectWithTags) { + CBORObject newObject = CBORObject.FromObject(objectValue); + if (!objectWithTags.IsTagged) { + return newObject; + } + objectWithTags = objectWithTags.UntagOne(); + if (!objectWithTags.IsTagged) { + return newObject; + } + EInteger[] tags = objectWithTags.GetAllTags(); + for (int i = tags.Length - 1; i >= 0; --i) { + newObject = CBORObject.FromObjectAndTag(newObject, tags[i]); + } + return newObject; + } + + internal static CBORObject ConvertToBigNum(CBORObject o, bool negative) { + if (o.Type != CBORType.ByteString) { + throw new CBORException("Byte array expected"); + } + byte[] data = o.GetByteString(); + if (data.Length <= 7) { + long x = 0; + for (var i = 0; i < data.Length; ++i) { + x <<= 8; + x |= ((long)data[i]) & 0xff; + } + if (negative) { + x = -x; + --x; + } + return FromObjectAndInnerTags(x, o); + } + int neededLength = data.Length; + byte[] bytes; + EInteger bi; + var extended = false; + if (((data[0] >> 7) & 1) != 0) { + // Increase the needed length + // if the highest bit is set, to + // distinguish negative and positive + // values + ++neededLength; + extended = true; + } + bytes = new byte[neededLength]; + for (var i = 0; i < data.Length; ++i) { + bytes[i] = data[data.Length - 1 - i]; + if (negative) { + bytes[i] = (byte)((~((int)bytes[i])) & 0xff); + } + } + if (extended) { + bytes[bytes.Length - 1] = negative ? (byte)0xff : (byte)0; + } + bi = EInteger.FromBytes(bytes, true); + // NOTE: Here, any tags are discarded; when called from + // the Read method, "o" will have no tags anyway (beyond tag 2), + // and when called from FromObjectAndTag, we prefer + // flexibility over throwing an error if the input + // object contains other tags. The tag 2 is also discarded + // because we are returning a "natively" supported CBOR object. + return CBORObject.FromObject(bi); + } + + public CBORObject ValidateObject(CBORObject obj) { + return ConvertToBigNum(obj, false); + } + } +} diff --git a/PeterO/Cbor/CBORTag26.cs b/PeterO/Cbor/CBORTag26.cs new file mode 100644 index 00000000..85e0fda3 --- /dev/null +++ b/PeterO/Cbor/CBORTag26.cs @@ -0,0 +1,25 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +#pragma warning disable 618 + +namespace PeterO.Cbor { + internal class CBORTag26 : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return new CBORTypeFilter().WithArrayMinLength(1, CBORTypeFilter.Any); + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.Array || obj.Count < 1) { + throw new CBORException("Not an array, or is an empty array."); + } + return obj; + } + } +} diff --git a/PeterO/Cbor/CBORTag27.cs b/PeterO/Cbor/CBORTag27.cs new file mode 100644 index 00000000..8e6dad5a --- /dev/null +++ b/PeterO/Cbor/CBORTag27.cs @@ -0,0 +1,25 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +#pragma warning disable 618 + +namespace PeterO.Cbor { + internal class CBORTag27 : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return new CBORTypeFilter().WithArrayMinLength(1, CBORTypeFilter.Any); + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.Array || obj.Count < 1) { + throw new CBORException("Not an array, or is an empty array."); + } + return obj; + } + } +} diff --git a/PeterO/Cbor/CBORTag3.cs b/PeterO/Cbor/CBORTag3.cs new file mode 100644 index 00000000..d2a6d640 --- /dev/null +++ b/PeterO/Cbor/CBORTag3.cs @@ -0,0 +1,23 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +#pragma warning disable 618 +namespace PeterO.Cbor { + /// + internal class CBORTag3 : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.ByteString; + } + + public CBORObject ValidateObject(CBORObject obj) { + return CBORTag2.ConvertToBigNum(obj, true); + } + } +} diff --git a/PeterO/Cbor/CBORTag30.cs b/PeterO/Cbor/CBORTag30.cs new file mode 100644 index 00000000..84655984 --- /dev/null +++ b/PeterO/Cbor/CBORTag30.cs @@ -0,0 +1,51 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTag30 : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return new CBORTypeFilter().WithArrayExactLength( + 2, + CBORTypeFilter.UnsignedInteger.WithNegativeInteger().WithTags(2, 3), + CBORTypeFilter.UnsignedInteger.WithTags(2)); + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.Array) { + throw new CBORException("Rational number must be an array"); + } + if (obj.Count != 2) { + throw new CBORException("Rational number requires exactly 2 items"); + } + CBORObject first = obj[0]; + CBORObject second = obj[1]; + if (!first.IsIntegral) { + throw new CBORException("Rational number requires integer numerator"); + } + if (!second.IsIntegral) { + throw new CBORException("Rational number requires integer denominator"); + } + if (second.Sign <= 0) { +throw new CBORException("Rational number requires denominator greater than 0"); + } + EInteger denom = second.AsEInteger(); + // NOTE: Discards tags. See comment in CBORTag2. + return denom.Equals(EInteger.One) ? + CBORObject.FromObject(first.AsEInteger()) : + CBORObject.FromObject( + ERational.Create( + first.AsEInteger(), + denom)); + } + } +} diff --git a/PeterO/Cbor/CBORTag32.cs b/PeterO/Cbor/CBORTag32.cs new file mode 100644 index 00000000..860ee860 --- /dev/null +++ b/PeterO/Cbor/CBORTag32.cs @@ -0,0 +1,40 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTag32 : ICBORTag, ICBORConverter + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.TextString; + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.TextString) { + throw new CBORException("URI must be a text string"); + } + if (!URIUtility.isValidIRI(obj.AsString())) { + throw new CBORException("String is not a valid URI/IRI"); + } + return obj; + } + + internal static void AddConverter() { + CBORObject.AddConverter(typeof(Uri), new CBORTag32()); + } + + public CBORObject ToCBORObject(Uri uri) { + if (uri == null) { + throw new ArgumentNullException(nameof(uri)); + } + string uriString = uri.ToString(); + return CBORObject.FromObjectAndTag(uriString, (int)32); + } + } +} diff --git a/PeterO/Cbor/CBORTag37.cs b/PeterO/Cbor/CBORTag37.cs new file mode 100644 index 00000000..927196c4 --- /dev/null +++ b/PeterO/Cbor/CBORTag37.cs @@ -0,0 +1,61 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + #pragma warning disable 618 + + internal class CBORTag37 : ICBORTag, ICBORToFromConverter + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.ByteString; + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.ByteString) { + throw new CBORException("UUID must be a byte string"); + } + byte[] bytes = obj.GetByteString(); + if (bytes.Length != 16) { + throw new CBORException("UUID must be 16 bytes long"); + } + return obj; + } + + internal static void AddConverter() { + CBORObject.AddConverter(typeof(Guid), new CBORTag37()); + } + + /// + public CBORObject ToCBORObject(Guid obj) { + byte[] bytes = PropertyMap.UUIDToBytes(obj); + return CBORObject.FromObjectAndTag(bytes, (int)37); + } + + public Guid FromCBORObject(CBORObject obj) { + if (!obj.HasMostOuterTag(37)) { + throw new CBORException("Must have outermost tag 37"); + } + this.ValidateObject(obj); + byte[] bytes = obj.GetByteString(); + var guidChars = new char[36]; + string hex = "0123456789abcdef"; + var index = 0; + for (var i = 0; i < 16; ++i) { + if (i == 4 || i == 6 || i == 8 || i == 10) { + guidChars[index++] = '-'; + } + guidChars[index++] = hex[(int)(bytes[i] >> 4) & 15]; + guidChars[index++] = hex[(int)bytes[i] & 15]; + } + string guidString = new String(guidChars); + return new Guid(guidString); + } + } +} diff --git a/PeterO/Cbor/CBORTag4.cs b/PeterO/Cbor/CBORTag4.cs new file mode 100644 index 00000000..78c7ffdc --- /dev/null +++ b/PeterO/Cbor/CBORTag4.cs @@ -0,0 +1,30 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTag4 : ICBORTag + { + public CBORTag4() : this(false) { + } + + private readonly bool extended; + + public CBORTag4(bool extended) { + this.extended = extended; + } + + public CBORTypeFilter GetTypeFilter() { + return this.extended ? CBORTag5.ExtendedFilter : CBORTag5.Filter; + } + + public CBORObject ValidateObject(CBORObject obj) { + return CBORTag5.ConvertToDecimalFrac(obj, true, this.extended); + } + } +} diff --git a/PeterO/Cbor/CBORTag5.cs b/PeterO/Cbor/CBORTag5.cs new file mode 100644 index 00000000..1b71af12 --- /dev/null +++ b/PeterO/Cbor/CBORTag5.cs @@ -0,0 +1,75 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTag5 : ICBORTag + { + internal static readonly CBORTypeFilter Filter = new + CBORTypeFilter().WithArrayExactLength( + 2, + CBORTypeFilter.UnsignedInteger.WithNegativeInteger(), + CBORTypeFilter.UnsignedInteger.WithNegativeInteger().WithTags(2, 3)); + + internal static readonly CBORTypeFilter ExtendedFilter = new + CBORTypeFilter().WithArrayExactLength( + 2, + CBORTypeFilter.UnsignedInteger.WithNegativeInteger().WithTags(2, 3), + CBORTypeFilter.UnsignedInteger.WithNegativeInteger().WithTags(2, 3)); + + public CBORTag5() : this(false) { + } + + private readonly bool extended; + + public CBORTag5(bool extended) { + this.extended = extended; + } + + public CBORTypeFilter GetTypeFilter() { + return this.extended ? ExtendedFilter : Filter; + } + + internal static CBORObject ConvertToDecimalFrac( + CBORObject o, + bool isDecimal, + bool extended) { + if (o.Type != CBORType.Array) { + throw new CBORException("Big fraction must be an array"); + } + if (o.Count != 2) { + throw new CBORException("Big fraction requires exactly 2 items"); + } + if (!o[0].IsIntegral) { + throw new CBORException("Exponent is not an integer"); + } + if (!o[1].IsIntegral) { + throw new CBORException("Mantissa is not an integer"); + } + EInteger exponent = o[0].AsEInteger(); + EInteger mantissa = o[1].AsEInteger(); + if (exponent.GetSignedBitLength() > 64 && !extended) { + throw new CBORException("Exponent is too big"); + } + if (exponent.IsZero) { + // Exponent is 0, so return mantissa instead + return CBORObject.FromObject(mantissa); + } + // NOTE: Discards tags. See comment in CBORTag2. + return isDecimal ? + CBORObject.FromObject(EDecimal.Create(mantissa, exponent)) : + CBORObject.FromObject(EFloat.Create(mantissa, exponent)); + } + + public CBORObject ValidateObject(CBORObject obj) { + return ConvertToDecimalFrac(obj, false, this.extended); + } + } +} diff --git a/PeterO/Cbor/CBORTagAny.cs b/PeterO/Cbor/CBORTagAny.cs new file mode 100644 index 00000000..29b94f0c --- /dev/null +++ b/PeterO/Cbor/CBORTagAny.cs @@ -0,0 +1,27 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +#pragma warning disable 618 + +namespace PeterO.Cbor { + internal class CBORTagAny : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.Any; + } + + public CBORObject ValidateObject(CBORObject obj) { + #if DEBUG + if (obj == null) { + throw new ArgumentNullException(nameof(obj)); + } + #endif + return obj; + } + } +} diff --git a/PeterO/Cbor/CBORTagGenericString.cs b/PeterO/Cbor/CBORTagGenericString.cs new file mode 100644 index 00000000..6b38a39d --- /dev/null +++ b/PeterO/Cbor/CBORTagGenericString.cs @@ -0,0 +1,27 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +#pragma warning disable 618 +namespace PeterO.Cbor { + /// + internal class CBORTagGenericString : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.TextString; + } + + public CBORObject ValidateObject(CBORObject obj) { + if (obj.Type == CBORType.TextString) { + throw new CBORException("Not a text string"); + } + return obj; + } + } +} diff --git a/PeterO/Cbor/CBORTagUnsigned.cs b/PeterO/Cbor/CBORTagUnsigned.cs new file mode 100644 index 00000000..a2694411 --- /dev/null +++ b/PeterO/Cbor/CBORTagUnsigned.cs @@ -0,0 +1,30 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +#pragma warning disable 618 +namespace PeterO.Cbor { + internal class CBORTagUnsigned : ICBORTag + { + public CBORTypeFilter GetTypeFilter() { + return CBORTypeFilter.UnsignedInteger; + } + + public CBORObject ValidateObject(CBORObject obj) { + #if DEBUG + if (obj == null) { + throw new ArgumentNullException(nameof(obj)); + } + #endif + if (!obj.IsIntegral || !obj.CanFitInInt64() || obj.Sign < 0) { + throw new CBORException("Not a 64-bit unsigned integer"); + } + return obj; + } + } +} diff --git a/PeterO/Cbor/CBORType.cs b/PeterO/Cbor/CBORType.cs new file mode 100644 index 00000000..25559c3b --- /dev/null +++ b/PeterO/Cbor/CBORType.cs @@ -0,0 +1,42 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + /// + public enum CBORType { + /// + Number, + + /// + Boolean, + + /// + SimpleValue, + + /// + ByteString, + + /// + TextString, + + /// + Array, + + /// + Map + } +} diff --git a/PeterO/Cbor/CBORTypeFilter.cs b/PeterO/Cbor/CBORTypeFilter.cs new file mode 100644 index 00000000..c3cf5768 --- /dev/null +++ b/PeterO/Cbor/CBORTypeFilter.cs @@ -0,0 +1,399 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + /// + [Obsolete("May be removed without replacement.")] + public sealed class CBORTypeFilter { + /// + public static readonly CBORTypeFilter Any = new CBORTypeFilter().WithAny(); + + /// + public static readonly CBORTypeFilter ByteString = new + CBORTypeFilter().WithByteString(); + + /// + public static readonly CBORTypeFilter NegativeInteger = new + CBORTypeFilter().WithNegativeInteger(); + + /// + public static readonly CBORTypeFilter None = new CBORTypeFilter(); + + /// + public static readonly CBORTypeFilter TextString = new + CBORTypeFilter().WithTextString(); + + /// + public static readonly CBORTypeFilter UnsignedInteger = new + CBORTypeFilter().WithUnsignedInteger(); + + private bool any; + private bool anyArrayLength; + private int arrayLength; + private bool arrayMinLength; + private CBORTypeFilter[] elements; + private bool floatingpoint; + private EInteger[] tags; + private int types; + + /// + public bool ArrayIndexAllowed(int index) { + return (this.types & (1 << 4)) != 0 && index >= 0 && + (this.anyArrayLength || + ((this.arrayMinLength || index < this.arrayLength) && index >= + 0)); + } + + /// + public bool ArrayLengthMatches(int length) { + return (this.types & (1 << 4)) != 0 && (this.anyArrayLength || + (this.arrayMinLength ? this.arrayLength >= length : + this.arrayLength == length)); + } + + /// + public bool ArrayLengthMatches(long length) { + return (this.types & (1 << 4)) != 0 && (this.anyArrayLength || + (this.arrayMinLength ? this.arrayLength >= length : + this.arrayLength == length)); + } + + /// + public bool ArrayLengthMatches(EInteger bigLength) { + if (bigLength == null) { + throw new ArgumentNullException(nameof(bigLength)); + } + return ((this.types & (1 << 4)) == 0) && (this.anyArrayLength || + ((!this.arrayMinLength && + bigLength.CompareTo((EInteger)this.arrayLength) == 0) || + (this.arrayMinLength && + bigLength.CompareTo((EInteger)this.arrayLength) >= 0))); + } + + /// + public CBORTypeFilter GetSubFilter(int index) { + if (this.anyArrayLength || this.any) { + return Any; + } + if (index < 0) { + return None; + } + if (index >= this.arrayLength) { + // Index is out of bounds + return this.arrayMinLength ? Any : None; + } + if (this.elements == null) { + return Any; + } + if (index >= this.elements.Length) { + // Index is greater than the number of elements for + // which a type is defined + return Any; + } + return this.elements[index]; + } + + /// + public CBORTypeFilter GetSubFilter(long index) { + if (this.anyArrayLength || this.any) { + return Any; + } + if (index < 0) { + return None; + } + if (index >= this.arrayLength) { + // Index is out of bounds + return this.arrayMinLength ? Any : None; + } + if (this.elements == null) { + return Any; + } + // NOTE: Index shouldn't be greater than Int32.MaxValue, + // since the length is an int + if (index >= this.elements.Length) { + // Index is greater than the number of elements for + // which a type is defined + return Any; + } + return this.elements[(int)index]; + } + + /// + public bool MajorTypeMatches(int type) { +#if DEBUG + if (type < 0) { + throw new ArgumentException("type (" + type + ") is less than 0"); + } + if (type > 7) { + throw new ArgumentException("type (" + type + ") is more than " + "7"); + } +#endif + + return type >= 0 && type <= 7 && (this.types & (1 << type)) != 0; + } + + /// + public bool NonFPSimpleValueAllowed() { + return this.MajorTypeMatches(7) && !this.floatingpoint; + } + + /// + public bool TagAllowed(int tag) { + return this.any || this.TagAllowed((EInteger)tag); + } + + /// + public bool TagAllowed(long longTag) { + return this.any || this.TagAllowed((EInteger)longTag); + } + + /// + public bool TagAllowed(EInteger bigTag) { + if (bigTag == null) { + throw new ArgumentNullException(nameof(bigTag)); + } + if (bigTag.Sign < 0) { + return false; + } + if (this.any) { + return true; + } + if ((this.types & (1 << 6)) == 0) { + return false; + } + if (this.tags == null) { + return true; + } + foreach (EInteger tag in this.tags) { + if (bigTag.Equals(tag)) { + return true; + } + } + return false; + } + + /// + public CBORTypeFilter WithArrayAnyLength() { + if (this.any) { + return this; + } + if (this.arrayLength < 0) { + throw new ArgumentException("this.arrayLength (" + this.arrayLength + + ") is less than 0"); + } + if (this.arrayLength < this.elements.Length) { + throw new ArgumentException("this.arrayLength (" + this.arrayLength + + ") is less than " + this.elements.Length); + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << 4; + filter.anyArrayLength = true; + return filter; + } + + /// + public CBORTypeFilter WithArrayExactLength( + int arrayLength, + params CBORTypeFilter[] elements) { + if (this.any) { + return this; + } + if (elements == null) { + throw new ArgumentNullException(nameof(elements)); + } + if (arrayLength < 0) { + throw new ArgumentException("arrayLength (" + arrayLength + + ") is less than 0"); + } + if (arrayLength < elements.Length) { + throw new ArgumentException("arrayLength (" + arrayLength + + ") is less than " + elements.Length); + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << 4; + filter.arrayLength = arrayLength; + filter.arrayMinLength = false; + filter.elements = new CBORTypeFilter[elements.Length]; + Array.Copy(elements, filter.elements, elements.Length); + return filter; + } + + /// + public CBORTypeFilter WithArrayMinLength( + int arrayLength, + params CBORTypeFilter[] elements) { + if (this.any) { + return this; + } + if (elements == null) { + throw new ArgumentNullException(nameof(elements)); + } + if (arrayLength < 0) { + throw new ArgumentException("arrayLength (" + arrayLength + + ") is less than 0"); + } + if (arrayLength < elements.Length) { + throw new ArgumentException("arrayLength (" + arrayLength + + ") is less than " + elements.Length); + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << 4; + filter.arrayLength = arrayLength; + filter.arrayMinLength = true; + filter.elements = new CBORTypeFilter[elements.Length]; + Array.Copy(elements, filter.elements, elements.Length); + return filter; + } + + /// + public CBORTypeFilter WithByteString() { + return this.WithType(2).WithTags(25); + } + + /// + public CBORTypeFilter WithFloatingPoint() { + if (this.any) { + return this; + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << 4; + filter.floatingpoint = true; + return filter; + } + + /// + public CBORTypeFilter WithMap() { + return this.WithType(5); + } + + /// + public CBORTypeFilter WithNegativeInteger() { + return this.WithType(1); + } + + /// + public CBORTypeFilter WithTags(params int[] tags) { + if (this.any) { + return this; + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << 6; // Always include the "tag" major type + var startIndex = 0; + if (filter.tags != null) { + var newTags = new EInteger[tags.Length + filter.tags.Length]; + Array.Copy(filter.tags, newTags, filter.tags.Length); + startIndex = filter.tags.Length; + filter.tags = newTags; + } else { + filter.tags = new EInteger[tags.Length]; + } + for (var i = 0; i < tags.Length; ++i) { + filter.tags[startIndex + i] = (EInteger)tags[i]; + } + return filter; + } + + internal CBORTypeFilter WithTags(params EInteger[] tags) { + if (this.any) { + return this; + } + for (var i = 0; i < tags.Length; ++i) { + if (tags[i] == null) { + throw new ArgumentNullException(nameof(tags)); + } + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << 6; // Always include the "tag" major type + var startIndex = 0; + if (filter.tags != null) { + var newTags = new EInteger[tags.Length + filter.tags.Length]; + Array.Copy(filter.tags, newTags, filter.tags.Length); + startIndex = filter.tags.Length; + filter.tags = newTags; + } else { + filter.tags = new EInteger[tags.Length]; + } + Array.Copy(tags, 0, filter.tags, startIndex, tags.Length); + return filter; + } + + /// + public CBORTypeFilter WithTextString() { + return this.WithType(3).WithTags(25); + } + + /// + public CBORTypeFilter WithUnsignedInteger() { + return this.WithType(0); + } + + private CBORTypeFilter Copy() { + var filter = new CBORTypeFilter(); + filter.any = this.any; + filter.types = this.types; + filter.floatingpoint = this.floatingpoint; + filter.arrayLength = this.arrayLength; + filter.anyArrayLength = this.anyArrayLength; + filter.arrayMinLength = this.arrayMinLength; + filter.elements = this.elements; + filter.tags = this.tags; + return filter; + } + + private CBORTypeFilter WithAny() { + if (this.any) { + return this; + } + CBORTypeFilter filter = this.Copy(); + filter.any = true; + filter.anyArrayLength = true; + filter.types = 0xff; + return filter; + } + + private CBORTypeFilter WithType(int type) { + if (this.any) { + return this; + } + CBORTypeFilter filter = this.Copy(); + filter.types |= 1 << type; + return filter; + } + } +} diff --git a/PeterO/Cbor/CBORTypeMapper.cs b/PeterO/Cbor/CBORTypeMapper.cs new file mode 100644 index 00000000..2986b841 --- /dev/null +++ b/PeterO/Cbor/CBORTypeMapper.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; + +namespace PeterO.Cbor { + /// + public sealed class CBORTypeMapper { + private readonly IList typePrefixes; + private readonly IList typeNames; + private readonly IDictionary + converters; + + /// Initializes a new instance of the CBORTypeMapper + /// class. + public CBORTypeMapper() { + this.typePrefixes = new List(); + this.typeNames = new List(); + this.converters = new Dictionary(); + } + + /// Registers an object that converts objects of a given type + /// to CBOR objects (called a CBOR converter). + /// A Type object specifying the type that the + /// converter converts to CBOR objects. + /// The parameter + /// is an ICBORConverter object. + /// Must be the same as the "type" + /// parameter. + /// This object. + /// The parameter + /// or is + /// null. + /// "Converter doesn't + /// contain a proper ToCBORObject method". + public CBORTypeMapper AddConverter( + Type type, + ICBORConverter converter) { + if (type == null) { + throw new ArgumentNullException(nameof(type)); + } + if (converter == null) { + throw new ArgumentNullException(nameof(converter)); + } + var ci = new ConverterInfo(); + ci.Converter = converter; + ci.ToObject = PropertyMap.FindOneArgumentMethod( + converter, + "ToCBORObject", + type); + if (ci.ToObject == null) { + throw new ArgumentException( + "Converter doesn't contain a proper ToCBORObject method"); + } + ci.FromObject = PropertyMap.FindOneArgumentMethod( + converter, + "FromCBORObject", + typeof(CBORObject)); + this.converters[type] = ci; + return this; + } + + internal object ConvertBackWithConverter( + CBORObject cbor, + Type type) { + ConverterInfo convinfo = null; + if (this.converters.ContainsKey(type)) { + convinfo = this.converters[type]; + } else { + return null; + } + if (convinfo == null) { + return null; + } + if (convinfo.FromObject == null) { + return null; + } + return PropertyMap.InvokeOneArgumentMethod( + convinfo.FromObject, + convinfo.Converter, + cbor); + } + + internal CBORObject ConvertWithConverter(object obj) { + Object type = obj.GetType(); + ConverterInfo convinfo = null; + if (this.converters.ContainsKey(type)) { + convinfo = this.converters[type]; + } else { + return null; + } + if (convinfo == null) { + return null; + } + return (CBORObject)PropertyMap.InvokeOneArgumentMethod( + convinfo.ToObject, + convinfo.Converter, + obj); + } + + /// + public bool FilterTypeName(string typeName) { + if (String.IsNullOrEmpty(typeName)) { + return false; + } + foreach (string prefix in this.typePrefixes) { + if (typeName.Length >= prefix.Length && + typeName.Substring(0, prefix.Length).Equals(prefix)) { + return true; + } + } + foreach (string name in this.typeNames) { + if (typeName.Equals(name)) { + return true; + } + } + return false; + } + + /// + public CBORTypeMapper AddTypePrefix(string prefix) { + if (prefix == null) { + throw new ArgumentNullException(nameof(prefix)); + } + if (prefix.Length == 0) { + throw new ArgumentException("prefix" + " is empty."); + } + this.typePrefixes.Add(prefix); + return this; + } + + /// + public CBORTypeMapper AddTypeName(string name) { + if (name == null) { + throw new ArgumentNullException(nameof(name)); + } + if (name.Length == 0) { + throw new ArgumentException("name" + " is empty."); + } + this.typeNames.Add(name); + return this; + } + + internal sealed class ConverterInfo { + /// + public object ToObject { get; set; } + + public object FromObject { get; set; } + + /// Gets a value not documented yet. + /// An internal API value. + public object Converter { get; set; } + } + } +} diff --git a/PeterO/Cbor/CBORUriConverter.cs b/PeterO/Cbor/CBORUriConverter.cs new file mode 100644 index 00000000..7101defc --- /dev/null +++ b/PeterO/Cbor/CBORUriConverter.cs @@ -0,0 +1,43 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + internal class CBORUriConverter : ICBORToFromConverter + { + private CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.TextString) { + throw new CBORException("URI must be a text string"); + } + if (!URIUtility.isValidIRI(obj.AsString())) { + throw new CBORException("String is not a valid URI/IRI"); + } + return obj; + } + + public Uri FromCBORObject(CBORObject obj) { + if (obj.HasMostOuterTag(32)) { + this.ValidateObject(obj); + try { + return new Uri(obj.AsString()); + } catch (Exception ex) { + throw new CBORException(ex.Message, ex); + } + } + throw new CBORException(); + } + + public CBORObject ToCBORObject(Uri uri) { + if (uri == null) { + throw new ArgumentNullException(nameof(uri)); + } + string uriString = uri.ToString(); + return CBORObject.FromObjectAndTag(uriString, (int)32); + } + } +} diff --git a/PeterO/Cbor/CBORUtilities.cs b/PeterO/Cbor/CBORUtilities.cs new file mode 100644 index 00000000..d8b74f52 --- /dev/null +++ b/PeterO/Cbor/CBORUtilities.cs @@ -0,0 +1,719 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Text; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + /// + internal static class CBORUtilities { + private const string HexAlphabet = "0123456789ABCDEF"; + + public static void ToBase16(StringBuilder str, byte[] data) { + if (data == null) { + throw new ArgumentNullException(nameof(data)); + } + int length = data.Length; + for (var i = 0; i < length; ++i) { + str.Append(HexAlphabet[(data[i] >> 4) & 15]); + str.Append(HexAlphabet[data[i] & 15]); + } + } + + public static bool ByteArrayEquals(byte[] a, byte[] b) { + if (a == null) { + return b == null; + } + if (b == null) { + return false; + } + if (a.Length != b.Length) { + return false; + } + for (var i = 0; i < a.Length; ++i) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + public static int ByteArrayHashCode(byte[] a) { + if (a == null) { + return 0; + } + var ret = 19; + unchecked { + ret = (ret * 31) + a.Length; + for (var i = 0; i < a.Length; ++i) { + ret = (ret * 31) + a[i]; + } + } + return ret; + } + + public static int ByteArrayCompare(byte[] a, byte[] b) { + if (a == null) { + return (b == null) ? 0 : -1; + } + if (b == null) { + return 1; + } + int c = Math.Min(a.Length, b.Length); + for (var i = 0; i < c; ++i) { + if (a[i] != b[i]) { + return (a[i] < b[i]) ? -1 : 1; + } + } + return (a.Length != b.Length) ? ((a.Length < b.Length) ? -1 : 1) : 0; + } + + public static string DoubleToString(double dbl) { + return EFloat.FromDouble(dbl).ToShortestString(EContext.Binary64); + } + + public static string SingleToString(float sing) { + return EFloat.FromSingle(sing).ToShortestString(EContext.Binary32); + } + + public static EInteger BigIntegerFromSingle(float flt) { + int value = BitConverter.ToInt32(BitConverter.GetBytes((float)flt), 0); + var fpexponent = (int)((value >> 23) & 0xff); + if (fpexponent == 255) { + throw new OverflowException("Value is infinity or NaN"); + } + int mantissa = value & 0x7fffff; + if (fpexponent == 0) { + ++fpexponent; + } else { + mantissa |= 1 << 23; + } + if (mantissa == 0) { + return EInteger.Zero; + } + fpexponent -= 150; + while ((mantissa & 1) == 0) { + ++fpexponent; + mantissa >>= 1; + } + bool neg = (value >> 31) != 0; + if (fpexponent == 0) { + if (neg) { + mantissa = -mantissa; + } + return (EInteger)mantissa; + } + if (fpexponent > 0) { + // Value is an integer + var bigmantissa = (EInteger)mantissa; + bigmantissa <<= fpexponent; + if (neg) { + bigmantissa = -(EInteger)bigmantissa; + } + return bigmantissa; + } else { + // Value has a fractional part + int exp = -fpexponent; + for (var i = 0; i < exp && mantissa != 0; ++i) { + mantissa >>= 1; + } + return (EInteger)mantissa; + } + } + + public static string LongToString(long longValue) { + if (longValue == Int64.MinValue) { + return "-9223372036854775808"; + } + if (longValue == 0L) { + return "0"; + } + if (longValue == (long)Int32.MinValue) { + return "-2147483648"; + } + bool neg = longValue < 0; + var count = 0; + char[] chars; + int intlongValue = unchecked((int)longValue); + if ((long)intlongValue == longValue) { + chars = new char[12]; + count = 11; + if (neg) { + intlongValue = -intlongValue; + } + while (intlongValue > 43698) { + int intdivValue = intlongValue / 10; + char digit = HexAlphabet[(int)(intlongValue - (intdivValue * 10))]; + chars[count--] = digit; + intlongValue = intdivValue; + } + while (intlongValue > 9) { + int intdivValue = (intlongValue * 26215) >> 18; + char digit = HexAlphabet[(int)(intlongValue - (intdivValue * 10))]; + chars[count--] = digit; + intlongValue = intdivValue; + } + if (intlongValue != 0) { + chars[count--] = HexAlphabet[(int)intlongValue]; + } + if (neg) { + chars[count] = '-'; + } else { + ++count; + } + return new String(chars, count, 12 - count); + } else { + chars = new char[24]; + count = 23; + if (neg) { + longValue = -longValue; + } + while (longValue > 43698) { + long divValue = longValue / 10; + char digit = HexAlphabet[(int)(longValue - (divValue * 10))]; + chars[count--] = digit; + longValue = divValue; + } + while (longValue > 9) { + long divValue = (longValue * 26215) >> 18; + char digit = HexAlphabet[(int)(longValue - (divValue * 10))]; + chars[count--] = digit; + longValue = divValue; + } + if (longValue != 0) { + chars[count--] = HexAlphabet[(int)longValue]; + } + if (neg) { + chars[count] = '-'; + } else { + ++count; + } + return new String(chars, count, 24 - count); + } + } + + private static EInteger FloorDiv(EInteger a, EInteger n) { + return a.Sign >= 0 ? a.Divide(n) : EInteger.FromInt32(-1).Subtract( + EInteger.FromInt32(-1).Subtract(a).Divide(n)); + } + + private static EInteger FloorMod(EInteger a, EInteger n) { + return a.Subtract(FloorDiv(a, n).Multiply(n)); + } + + private static readonly int[] ValueNormalDays = { 0, 31, 28, 31, 30, 31, 30, + 31, 31, 30, + 31, 30, 31 }; + + private static readonly int[] ValueLeapDays = { 0, 31, 29, 31, 30, 31, 30, + 31, 31, 30, + 31, 30, 31 }; + + private static readonly int[] ValueNormalToMonth = { 0, 0x1f, 0x3b, 90, 120, + 0x97, 0xb5, + 0xd4, 0xf3, 0x111, 0x130, 0x14e, 0x16d }; + + private static readonly int[] ValueLeapToMonth = { 0, 0x1f, 60, 0x5b, 0x79, + 0x98, 0xb6, + 0xd5, 0xf4, 0x112, 0x131, 0x14f, 0x16e }; + + public static void GetNormalizedPartProlepticGregorian( + EInteger year, + int month, + EInteger day, + EInteger[] dest) { + // NOTE: This method assumes month is 1 to 12 + if (month <= 0 || month > 12) { + throw new ArgumentOutOfRangeException(nameof(month)); + } + EInteger num4 = EInteger.FromInt32(4); + EInteger num100 = EInteger.FromInt32(100); + EInteger num101 = EInteger.FromInt32(101); + EInteger num146097 = EInteger.FromInt32(146097); + EInteger num400 = EInteger.FromInt32(400); + int[] dayArray = (year.Remainder(num4).Sign != 0 || ( + year.Remainder(num100).Sign == 0 && + year.Remainder(num400).Sign != 0)) ? + ValueNormalDays : ValueLeapDays; + if (day.CompareTo(num101) > 0) { + EInteger count = day.Subtract(num100).Divide(num146097); + day = day.Subtract(count.Multiply(num146097)); + year = year.Add(count.Multiply(num400)); + } + while (true) { + EInteger days = EInteger.FromInt32(dayArray[month]); + if (day.Sign > 0 && day.CompareTo(days) <= 0) { + break; + } + if (day.CompareTo(days) > 0) { + day = day.Subtract(days); + if (month == 12) { + month = 1; + year = year.Add(EInteger.One); + dayArray = (year.Remainder(num4).Sign != 0 || ( + year.Remainder(num100).Sign == 0 && + year.Remainder(num400).Sign != 0)) ? ValueNormalDays : + ValueLeapDays; + } else { + ++month; + } + } + if (day.Sign <= 0) { + int divResult = (month - 2) / 12; + year = year.Add(EInteger.FromInt32(divResult)); + month = ((month - 2) - (12 * divResult)) + 1; + dayArray = (year.Remainder(num4).Sign != 0 || ( + year.Remainder(num100).Sign == 0 && + year.Remainder(num400).Sign != 0)) ? ValueNormalDays : + ValueLeapDays; + day = day.Add(EInteger.FromInt32(dayArray[month])); + } + } + dest[0] = year; + dest[1] = EInteger.FromInt32(month); + dest[2] = day; + } + + public static EInteger GetNumberOfDaysProlepticGregorian( + EInteger year, + int month, + int mday) { + // NOTE: month = 1 is January, year = 1 is year 1 + if (month <= 0 || month > 12) { + throw new ArgumentException(); +} + if (mday <= 0 || mday > 31) { + throw new ArgumentException(); +} + EInteger num4 = EInteger.FromInt32(4); + EInteger num100 = EInteger.FromInt32(100); + EInteger num400 = EInteger.FromInt32(400); + EInteger numDays = EInteger.Zero; + var startYear = 1970; + if (year.CompareTo(EInteger.FromInt32(startYear)) < 0) { + for (EInteger ei = EInteger.FromInt32(startYear - 1); + ei.CompareTo(year) > 0; + ei = ei.Subtract(EInteger.One)) { + numDays = numDays.Subtract(EInteger.FromInt32(365)); + if (!(ei.Remainder(num4).Sign != 0 || ( + ei.Remainder(num100).Sign == 0 && + ei.Remainder(num400).Sign != 0))) { + numDays = numDays.Subtract(EInteger.One); + } + } + if (year.Remainder(num4).Sign != 0 || ( + year.Remainder(num100).Sign == 0 && + year.Remainder(num400).Sign != 0)) { + numDays = numDays.Subtract( + EInteger.FromInt32(365 - ValueNormalToMonth[month])); + numDays = numDays.Subtract( + EInteger.FromInt32(ValueNormalDays[month] - mday + 1)); + } else { + numDays = numDays.Subtract( + EInteger.FromInt32(366 - ValueLeapToMonth[month])); + numDays = numDays.Subtract( + EInteger.FromInt32(ValueLeapDays[month] - mday + 1)); + } + } else { + bool isNormalYear = year.Remainder(num4).Sign != 0 || + (year.Remainder(num100).Sign == 0 && year.Remainder(num400).Sign != + 0); + EInteger ei = EInteger.FromInt32(startYear); + EInteger num401 = EInteger.FromInt32(401); + EInteger num146097 = EInteger.FromInt32(146097); + for (; ei.Add(num401).CompareTo(year) < 0; + ei = ei.Add(num400)) { + numDays = numDays.Add(num146097); + } + for (; ei.CompareTo(year) < 0; + ei = ei.Add(EInteger.One)) { + numDays = numDays.Add(EInteger.FromInt32(365)); + if (!(ei.Remainder(num4).Sign != 0 || ( + ei.Remainder(num100).Sign == 0 && + ei.Remainder(num400).Sign != 0))) { + numDays = numDays.Add(EInteger.One); + } + } + int yearToMonth = isNormalYear ? ValueNormalToMonth[month - 1] : + ValueLeapToMonth[month - 1]; + numDays = numDays.Add(EInteger.FromInt32(yearToMonth)) + .Add(EInteger.FromInt32(mday - 1)); + } + return numDays; + } + + public static void BreakDownSecondsSinceEpoch( + EDecimal edec, + EInteger[] year, + int[] lesserFields) { + EInteger integerPart = edec.ToEInteger(); + EDecimal fractionalPart = edec.Abs() + .Subtract(EDecimal.FromEInteger(integerPart).Abs()); + int nanoseconds = fractionalPart .Multiply(EDecimal.FromInt32(1000000000)) + .ToInt32Checked(); + var normPart = new EInteger[3]; + EInteger days = FloorDiv( + integerPart, + EInteger.FromInt32(86400)).Add(EInteger.One); + int secondsInDay = FloorMod( + integerPart, + EInteger.FromInt32(86400)).ToInt32Checked(); + GetNormalizedPartProlepticGregorian( + EInteger.FromInt32(1970), + 1, + days, + normPart); + lesserFields[0] = normPart[1].ToInt32Checked(); + lesserFields[1] = normPart[2].ToInt32Checked(); + lesserFields[2] = secondsInDay / 3600; + lesserFields[3] = (secondsInDay % 3600) / 60; + lesserFields[4] = secondsInDay % 60; + lesserFields[5] = nanoseconds / 100; + lesserFields[6] = 0; + year[0] = normPart[0]; + } + + public static bool NameStartsWithWord(String name, String word) { + int wl = word.Length; + return name.Length > wl && name.Substring(0, wl).Equals(word) && + !(name[wl] >= 'a' && name[wl] <= 'z') && + !(name[wl] >= '0' && name[wl] <= '9'); + } + + public static String FirstCharLower(String name) { + if (name.Length > 0 && name[0] >= 'A' && name[0] <= 'Z') { + var sb = new StringBuilder(); + sb.Append((char)(name[0] + 0x20)); + sb.Append(name.Substring(1)); + return sb.ToString(); + } + return name; + } + + public static String FirstCharUpper(String name) { + if (name.Length > 0 && name[0] >= 'a' && name[0] <= 'z') { + var sb = new StringBuilder(); + sb.Append((char)(name[0] - 0x20)); + sb.Append(name.Substring(1)); + return sb.ToString(); + } + return name; + } + + private static bool IsValidDateTime(int[] dateTime) { + if (dateTime == null || dateTime.Length < 8) { + return false; + } + if (dateTime[1] < 1 || dateTime[1] > 12 || dateTime[2] < 1) { + return false; + } + bool leap = IsLeapYear(dateTime[0]); + if (dateTime[1] == 4 || dateTime[1] == 6 || dateTime[1] == 9 || + dateTime[1] == 11) { + if (dateTime[2] > 30) { + return false; + } + } else if (dateTime[1] == 2) { + if (dateTime[2] > (leap ? 29 : 28)) { + return false; + } + } else { + if (dateTime[2] > 31) { + return false; + } + } + return !(dateTime[3] < 0 || dateTime[4] < 0 || dateTime[5] < 0 || +dateTime[3] >= 24 || dateTime[4] >= 60 || dateTime[5] >= 61 || +dateTime[6] < 0 || +dateTime[6] >= 1000000000 || dateTime[7] <= -1440 || + dateTime[7] >= 1440); + } + + private static bool IsLeapYear(int yr) { + yr %= 400; + if (yr < 0) { + yr += 400; + } + return (((yr % 4) == 0) && ((yr % 100) != 0)) || ((yr % 400) == 0); + } + + public static void ParseAtomDateTimeString( + string str, + EInteger[] bigYearArray, + int[] lf) { + int[] d = ParseAtomDateTimeString(str); + bigYearArray[0] = EInteger.FromInt32(d[0]); + Array.Copy(d, 1, lf, 0, 7); + } + + private static int[] ParseAtomDateTimeString(string str) { + var bad = false; + if (str.Length < 19) { + throw new ArgumentException("Invalid date/time"); + } + for (var i = 0; i < 19 && !bad; ++i) { + if (i == 4 || i == 7) { + bad |= str[i] != '-'; + } else if (i == 13 || i == 16) { + bad |= str[i] != ':'; + } else if (i == 10) { + bad |= str[i] != 'T'; + /*lowercase t not used to separate date/time, + following RFC 4287 sec. 3.3*/ } else { + bad |= str[i] < '0' || str[i] > +'9'; + } + } + if (bad) { + throw new ArgumentException("Invalid date/time"); + } + int year = ((str[0] - '0') * 1000) + ((str[1] - '0') * 100) + + (str[2] - '0') * 10 + (str[3] - '0'); + int month = ((str[5] - '0') * 10) + (str[6] - '0'); + int day = ((str[8] - '0') * 10) + (str[9] - '0'); + int hour = ((str[11] - '0') * 10) + (str[12] - '0'); + int minute = ((str[14] - '0') * 10) + (str[15] - '0'); + int second = ((str[17] - '0') * 10) + (str[18] - '0'); + var index = 19; + var nanoSeconds = 0; + if (index <= str.Length && str[index] == '.') { + var icount = 0; + ++index; + while (index < str.Length) { + if (str[index] < '0' || str[index] > '9') { + break; + } + if (icount < 9) { + nanoSeconds = nanoSeconds * 10 + (str[index] - '0'); + ++icount; + } + ++index; + } + while (icount < 9) { + nanoSeconds *= 10; + ++icount; + } + } + var utcToLocal = 0; + if (index + 1 == str.Length && str[index] == 'Z') { + /*lowercase z not used to indicate UTC, + following RFC 4287 sec. 3.3*/ + utcToLocal = 0; + } else if (index + 6 == str.Length) { + bad = false; + for (var i = 0; i < 6 && !bad; ++i) { + if (i == 0) { + bad |= str[index + i] != '-' && str[index + i] != '+'; + } else if (i == 3) { + bad |= str[index + i] != ':'; + } else { + bad |= str[index + i] < '0' || str[index + i] > '9'; + } + } + if (bad) { + throw new ArgumentException("Invalid date/time"); + } + bool neg = str[index] == '-'; + int tzhour = ((str[index + 1] - '0') * 10) + (str[index + 2] - '0'); + int tzminute = ((str[index + 4] - '0') * 10) + (str[index + 5] - '0'); + if (tzminute >= 60) { + throw new ArgumentException("Invalid date/time"); + } + utcToLocal = (neg ? -1 : 1) * (tzhour * 60) + tzminute; + } else { + throw new ArgumentException("Invalid date/time"); + } + int[] dt = new[] { year, month, day, hour, minute, second, + nanoSeconds, utcToLocal}; + if (!IsValidDateTime(dt)) { + throw new ArgumentException("Invalid date/time"); + } + return dt; + } + + public static string ToAtomDateTimeString( + EInteger bigYear, + int[] lesserFields, + bool fracIsNanoseconds) { + // TODO: fracIsNanoseconds is a parameter + // for compatibility purposes only + if (lesserFields[6] != 0) { + throw new NotSupportedException( + "Local time offsets not supported"); + } + int smallYear = bigYear.ToInt32Checked(); + if (smallYear < 0) { + throw new ArgumentException("year (" + smallYear + + ") is not greater or equal to 0"); +} +if (smallYear > 9999) { + throw new ArgumentException("year (" + smallYear + + ") is not less or equal to 9999"); +} + int month = lesserFields[0]; + int day = lesserFields[1]; + int hour = lesserFields[2]; + int minute = lesserFields[3]; + int second = lesserFields[4]; + int fracSeconds = lesserFields[5]; + var charbuf = new char[32]; + charbuf[0] = (char)('0' + ((smallYear / 1000) % 10)); + charbuf[1] = (char)('0' + ((smallYear / 100) % 10)); + charbuf[2] = (char)('0' + ((smallYear / 10) % 10)); + charbuf[3] = (char)('0' + (smallYear % 10)); + charbuf[4] = '-'; + charbuf[5] = (char)('0' + ((month / 10) % 10)); + charbuf[6] = (char)('0' + (month % 10)); + charbuf[7] = '-'; + charbuf[8] = (char)('0' + ((day / 10) % 10)); + charbuf[9] = (char)('0' + (day % 10)); + charbuf[10] = 'T'; + charbuf[11] = (char)('0' + ((hour / 10) % 10)); + charbuf[12] = (char)('0' + (hour % 10)); + charbuf[13] = ':'; + charbuf[14] = (char)('0' + ((minute / 10) % 10)); + charbuf[15] = (char)('0' + (minute % 10)); + charbuf[16] = ':'; + charbuf[17] = (char)('0' + ((second / 10) % 10)); + charbuf[18] = (char)('0' + (second % 10)); + var charbufLength = 19; + if (!fracIsNanoseconds) { + int milliseconds = fracSeconds / 1000000; + if (milliseconds > 0) { + charbuf[19] = '.'; + charbuf[20] = (char)('0' + ((milliseconds / 100) % 10)); + charbuf[21] = (char)('0' + ((milliseconds / 10) % 10)); + charbuf[22] = (char)('0' + (milliseconds % 10)); + charbuf[23] = 'Z'; + charbufLength += 5; + } else { + charbuf[19] = 'Z'; + ++charbufLength; + } + } else { + if (fracSeconds > 0) { + charbuf[19] = '.'; + ++charbufLength; +int digitdiv = 100000000; +int index = 20; +while (digitdiv > 0 && fracSeconds != 0) { + int digit = (fracSeconds / digitdiv) % 10; + fracSeconds -= digit * digitdiv; + charbuf[index++] = (char)('0' + digit); + ++charbufLength; + digitdiv /= 10; +} + charbuf[index] = 'Z'; + ++charbufLength; + } else { + charbuf[19] = 'Z'; + ++charbufLength; + } + } + return new String(charbuf, 0, charbufLength); + } + + public static EInteger BigIntegerFromDouble(double dbl) { + long lvalue = BitConverter.ToInt64( + BitConverter.GetBytes((double)dbl), + 0); + int value0 = unchecked((int)(lvalue & 0xffffffffL)); + int value1 = unchecked((int)((lvalue >> 32) & 0xffffffffL)); + var floatExponent = (int)((value1 >> 20) & 0x7ff); + bool neg = (value1 >> 31) != 0; + if (floatExponent == 2047) { + throw new OverflowException("Value is infinity or NaN"); + } + value1 &= 0xfffff; // Mask out the exponent and sign + if (floatExponent == 0) { + ++floatExponent; + } else { + value1 |= 0x100000; + } + if ((value1 | value0) != 0) { + while ((value0 & 1) == 0) { + value0 >>= 1; + value0 &= 0x7fffffff; + value0 = unchecked(value0 | (value1 << 31)); + value1 >>= 1; + ++floatExponent; + } + } + floatExponent -= 1075; + var bytes = new byte[9]; + EInteger bigmantissa; + bytes[0] = (byte)(value0 & 0xff); + bytes[1] = (byte)((value0 >> 8) & 0xff); + bytes[2] = (byte)((value0 >> 16) & 0xff); + bytes[3] = (byte)((value0 >> 24) & 0xff); + bytes[4] = (byte)(value1 & 0xff); + bytes[5] = (byte)((value1 >> 8) & 0xff); + bytes[6] = (byte)((value1 >> 16) & 0xff); + bytes[7] = (byte)((value1 >> 24) & 0xff); + bytes[8] = (byte)0; + bigmantissa = EInteger.FromBytes(bytes, true); + if (floatExponent == 0) { + if (neg) { + bigmantissa = -bigmantissa; + } + return bigmantissa; + } + if (floatExponent > 0) { + // Value is an integer + bigmantissa <<= floatExponent; + if (neg) { + bigmantissa = -(EInteger)bigmantissa; + } + return bigmantissa; + } else { + // Value has a fractional part + int exp = -floatExponent; + bigmantissa >>= exp; + if (neg) { + bigmantissa = -(EInteger)bigmantissa; + } + return bigmantissa; + } + } + + public static float HalfPrecisionToSingle(int value) { + int negvalue = (value >= 0x8000) ? (1 << 31) : 0; + value &= 0x7fff; + if (value >= 0x7c00) { + value = (int)(0x3fc00 | (value & 0x3ff)) << 13 | negvalue; + return BitConverter.ToSingle( + BitConverter.GetBytes(value), + 0); + } + if (value > 0x400) { + value = (int)((value + 0x1c000) << 13) | negvalue; + return BitConverter.ToSingle( + BitConverter.GetBytes(value), + 0); + } + if ((value & 0x400) == value) { + value = (int)((value == 0) ? 0 : 0x38800000) | negvalue; + return BitConverter.ToSingle( + BitConverter.GetBytes(value), + 0); + } else { + // denormalized + int m = value & 0x3ff; + value = 0x1c400; + while ((m >> 10) == 0) { + value -= 0x400; + m <<= 1; + } + value = ((value | (m & 0x3ff)) << 13) | negvalue; + return BitConverter.ToSingle(BitConverter.GetBytes((int)value), 0); + } + } + } +} diff --git a/PeterO/Cbor/CBORUuidConverter.cs b/PeterO/Cbor/CBORUuidConverter.cs new file mode 100644 index 00000000..6b75d65a --- /dev/null +++ b/PeterO/Cbor/CBORUuidConverter.cs @@ -0,0 +1,51 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + internal class CBORUuidConverter : ICBORToFromConverter + { + private CBORObject ValidateObject(CBORObject obj) { + if (obj.Type != CBORType.ByteString) { + throw new CBORException("UUID must be a byte string"); + } + byte[] bytes = obj.GetByteString(); + if (bytes.Length != 16) { + throw new CBORException("UUID must be 16 bytes long"); + } + return obj; + } + + /// + public CBORObject ToCBORObject(Guid obj) { + byte[] bytes = PropertyMap.UUIDToBytes(obj); + return CBORObject.FromObjectAndTag(bytes, (int)37); + } + + public Guid FromCBORObject(CBORObject obj) { + if (!obj.HasMostOuterTag(37)) { + throw new CBORException("Must have outermost tag 37"); + } + this.ValidateObject(obj); + byte[] bytes = obj.GetByteString(); + var guidChars = new char[36]; + string hex = "0123456789abcdef"; + var index = 0; + for (var i = 0; i < 16; ++i) { + if (i == 4 || i == 6 || i == 8 || i == 10) { + guidChars[index++] = '-'; + } + guidChars[index++] = hex[(int)(bytes[i] >> 4) & 15]; + guidChars[index++] = hex[(int)bytes[i] & 15]; + } + string guidString = new String(guidChars); + return new Guid(guidString); + } + } +} diff --git a/PeterO/Cbor/CharacterInputWithCount.cs b/PeterO/Cbor/CharacterInputWithCount.cs new file mode 100644 index 00000000..7efbd6af --- /dev/null +++ b/PeterO/Cbor/CharacterInputWithCount.cs @@ -0,0 +1,78 @@ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class CharacterInputWithCount : ICharacterInput { + private readonly ICharacterInput ci; + private int offset; + + public CharacterInputWithCount(ICharacterInput ci) { + this.ci = ci; + } + + public int GetOffset() { + return this.offset; + } + + public void RaiseError(string str) { + throw new CBORException(this.NewErrorString(str)); + } + + public int Read(int[] chars, int index, int length) { + if (chars == null) { + throw new ArgumentNullException(nameof(chars)); + } + if (index < 0) { + throw new ArgumentException("index (" + index + + ") is less than 0"); + } + if (index > chars.Length) { + throw new ArgumentException("index (" + index + + ") is more than " + chars.Length); + } + if (length < 0) { + throw new ArgumentException("length (" + length + + ") is less than 0"); + } + if (length > chars.Length) { + throw new ArgumentException("length (" + length + + ") is more than " + chars.Length); + } + if (chars.Length - index < length) { + throw new ArgumentException("chars's length minus " + index + " (" + + (chars.Length - index) + ") is less than " + length); + } + int ret = this.ci.Read(chars, index, length); + if (ret > 0) { + this.offset += ret; + } + return ret; + } + + public int ReadChar() { + var c = -1; + try { + c = this.ci.ReadChar(); + } catch (InvalidOperationException ex) { + if (ex.InnerException == null) { + throw new CBORException( + this.NewErrorString(ex.Message), + ex); + } else { + throw new CBORException( + this.NewErrorString(ex.Message), + ex.InnerException); + } + } + if (c >= 0) { + ++this.offset; + } + return c; + } + + private string NewErrorString(string str) { + return str + " (offset " + this.GetOffset() + ")"; + } + } +} diff --git a/PeterO/Cbor/CharacterReader.cs b/PeterO/Cbor/CharacterReader.cs new file mode 100644 index 00000000..fe9970cd --- /dev/null +++ b/PeterO/Cbor/CharacterReader.cs @@ -0,0 +1,743 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.IO; + +namespace PeterO.Cbor { + // + internal sealed class CharacterReader : ICharacterInput { + private readonly int mode; + private readonly bool errorThrow; + private readonly bool dontSkipUtf8Bom; + private readonly string str; + private readonly int strLength; + private readonly IByteReader stream; + + private int offset; + private ICharacterInput reader; + + // + public CharacterReader(string str) : this(str, false, false) { + } + + // + public CharacterReader(string str, bool skipByteOrderMark) : + this(str, skipByteOrderMark, false) { + } + + // + public CharacterReader( + string str, + bool skipByteOrderMark, + bool errorThrow) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + this.strLength = str.Length; + this.offset = (skipByteOrderMark && this.strLength > 0 && str[0] == + 0xfeff) ? 1 : 0; + this.str = str; + this.errorThrow = errorThrow; + this.mode = -1; + this.dontSkipUtf8Bom = false; + this.stream = null; + } + + // + public CharacterReader(string str, int offset, int length) : + this(str, offset, length, false, false) { + } + + // + public CharacterReader( + string str, + int offset, + int length, + bool skipByteOrderMark, + bool errorThrow) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); +} +if (offset < 0) { + throw new ArgumentException("offset (" + offset + + ") is less than 0"); +} +if (offset > str.Length) { + throw new ArgumentException("offset (" + offset + + ") is more than " + str.Length); +} +if (length < 0) { + throw new ArgumentException("length (" + length + + ") is less than 0"); +} +if (length > str.Length) { + throw new ArgumentException("length (" + length + + ") is more than " + str.Length); +} +if (str.Length - offset < length) { + throw new ArgumentException("str's length minus " + offset + " (" + + (str.Length - offset) + ") is less than " + length); +} + this.strLength = length; + this.offset = (skipByteOrderMark && length > 0 && str[offset] == + 0xfeff) ? offset + 1 : 0; + this.str = str; + this.errorThrow = errorThrow; + this.mode = -1; + this.dontSkipUtf8Bom = false; + this.stream = null; + } + + // + public CharacterReader(Stream stream) : this(stream, 0, false) { + } + + // + public CharacterReader(Stream stream, int mode, bool errorThrow) : + this(stream, mode, errorThrow, false) { + } + + // + public CharacterReader(Stream stream, int mode) : + this(stream, mode, false, false) { + } + + // + public CharacterReader( + Stream stream, + int mode, + bool errorThrow, + bool dontSkipUtf8Bom) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + this.stream = new WrappedStream(stream); + this.mode = mode; + this.errorThrow = errorThrow; + this.dontSkipUtf8Bom = dontSkipUtf8Bom; + this.str = String.Empty; + this.strLength = -1; + } + + private interface IByteReader { + int ReadByte(); + } + + // + public int Read(int[] chars, int index, int length) { + if (chars == null) { + throw new ArgumentNullException(nameof(chars)); + } + if (index < 0) { + throw new ArgumentException("index (" + index + + ") is less than 0"); + } + if (index > chars.Length) { + throw new ArgumentException("index (" + index + + ") is more than " + chars.Length); + } + if (length < 0) { + throw new ArgumentException("length (" + length + + ") is less than 0"); + } + if (length > chars.Length) { + throw new ArgumentException("length (" + length + + ") is more than " + chars.Length); + } + if (chars.Length - index < length) { + throw new ArgumentException("chars's length minus " + index + " (" + + (chars.Length - index) + ") is less than " + length); + } + var count = 0; + for (int i = 0; i < length; ++i) { + int c = this.ReadChar(); + if (c < 0) { + return count; + } + chars[index + i] = c; + ++count; + } + return count; + } + + // + public int ReadChar() { + if (this.reader != null) { + return this.reader.ReadChar(); + } + if (this.stream != null) { + return this.DetectUnicodeEncoding(); + } else { + int c = (this.offset < this.strLength) ? this.str[this.offset] : -1; + if ((c & 0xfc00) == 0xd800 && this.offset + 1 < this.strLength && + this.str[this.offset + 1] >= 0xdc00 && this.str[this.offset + 1] + <= 0xdfff) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (this.str[this.offset + 1] - + 0xdc00); + ++this.offset; + } else if ((c & 0xf800) == 0xd800) { + // unpaired surrogate + if (this.errorThrow) { + throw new InvalidOperationException("Unpaired surrogate code point"); +} else { + c = 0xfffd; +} + } + ++this.offset; + return c; + } + } + + private int DetectUtf8Or16Or32(int c1) { + int c2, c3, c4; + if (c1 == 0xff || c1 == 0xfe) { + // Start of a possible byte-order mark + // FF FE 0 0 --> UTF-32LE + // FF FE ... --> UTF-16LE + // FE FF --> UTF-16BE + c2 = this.stream.ReadByte(); + bool bigEndian = c1 == 0xfe; + int otherbyte = bigEndian ? 0xff : 0xfe; + if (c2 == otherbyte) { + c3 = this.stream.ReadByte(); + c4 = this.stream.ReadByte(); + if (!bigEndian && c3 == 0 && c4 == 0) { + this.reader = new Utf32Reader(this.stream, false, this.errorThrow); + return this.reader.ReadChar(); + } else { + var newReader = new Utf16Reader( + this.stream, + bigEndian, + this.errorThrow); + newReader.Unget(c3, c4); + this.reader = newReader; + return newReader.ReadChar(); + } + } + // Assume UTF-8 here, so the 0xff or 0xfe is invalid + if (this.errorThrow) { + throw new InvalidOperationException("Invalid Unicode stream"); + } else { + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.Unget(c2); + this.reader = utf8reader; + return 0xfffd; + } + } else if (c1 == 0 && this.mode == 4) { + // Here, the relevant cases are: + // 0 0 0 NZA --> UTF-32BE (if mode is 4) + // 0 0 FE FF --> UTF-32BE + // Anything else is treated as UTF-8 + c2 = this.stream.ReadByte(); + c3 = this.stream.ReadByte(); + c4 = this.stream.ReadByte(); + if (c2 == 0 && + ((c3 == 0xfe && c4 == 0xff) || + (c3 == 0 && c4 >= 0x01 && c4 <= 0x7f))) { + this.reader = new Utf32Reader(this.stream, true, this.errorThrow); + return c3 == 0 ? c4 : this.reader.ReadChar(); + } else { + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.UngetThree(c2, c3, c4); + this.reader = utf8reader; + return c1; + } + } else if (this.mode == 2) { + if (c1 >= 0x01 && c1 <= 0x7f) { + // Nonzero ASCII character + c2 = this.stream.ReadByte(); + if (c2 == 0) { + // NZA 0, so UTF-16LE or UTF-32LE + c3 = this.stream.ReadByte(); + c4 = this.stream.ReadByte(); + if (c3 == 0 && c4 == 0) { + this.reader = new Utf32Reader( + this.stream, + false, + this.errorThrow); + return c1; + } else { + var newReader = new Utf16Reader( + this.stream, + false, + this.errorThrow); + newReader.Unget(c3, c4); + this.reader = newReader; + return c1; + } + } else { + // NZA NZ, so UTF-8 + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.Unget(c2); + this.reader = utf8reader; + return c1; + } + } else if (c1 == 0) { + // Zero + c2 = this.stream.ReadByte(); + if (c2 >= 0x01 && c2 <= 0x7f) { + // 0 NZA, so UTF-16BE + var newReader = new Utf16Reader(this.stream, true, this.errorThrow); + this.reader = newReader; + return c2; + } else if (c2 == 0) { + // 0 0, so maybe UTF-32BE + c3 = this.stream.ReadByte(); + c4 = this.stream.ReadByte(); + if (c3 == 0 && c4 >= 0x01 && c4 <= 0x7f) { + // 0 0 0 NZA + this.reader = new Utf32Reader(this.stream, true, this.errorThrow); + return c4; + } else if (c3 == 0xfe && c4 == 0xff) { + // 0 0 FE FF + this.reader = new Utf32Reader(this.stream, true, this.errorThrow); + return this.reader.ReadChar(); + } else { + // 0 0 ... + var newReader = new Utf8Reader(this.stream, this.errorThrow); + newReader.UngetThree(c2, c3, c4); + this.reader = newReader; + return c1; + } + } else { + // 0 NonAscii, so UTF-8 + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.Unget(c2); + this.reader = utf8reader; + return c1; + } + } + } + // Use default of UTF-8 + return -2; + } + + private int DetectUtf8OrUtf16(int c1) { + int mode = this.mode; + int c2; + if (c1 == 0xff || c1 == 0xfe) { + c2 = this.stream.ReadByte(); + bool bigEndian = c1 == 0xfe; + int otherbyte = bigEndian ? 0xff : 0xfe; + if (c2 == otherbyte) { + var newReader = new Utf16Reader( + this.stream, + bigEndian, + this.errorThrow); + this.reader = newReader; + return newReader.ReadChar(); + } + // Assume UTF-8 here, so the 0xff or 0xfe is invalid + if (this.errorThrow) { + throw new InvalidOperationException("Invalid Unicode stream"); + } else { + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.Unget(c2); + this.reader = utf8reader; + return 0xfffd; + } + } else if (mode == 1) { + if (c1 >= 0x01 && c1 <= 0x7f) { + // Nonzero ASCII character + c2 = this.stream.ReadByte(); + if (c2 == 0) { + // NZA 0, so UTF-16LE + var newReader = new Utf16Reader( + this.stream, + false, + this.errorThrow); + this.reader = newReader; + } else { + // NZA NZ + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.Unget(c2); + this.reader = utf8reader; + } + return c1; + } else if (c1 == 0) { + // Zero + c2 = this.stream.ReadByte(); + if (c2 >= 0x01 && c2 <= 0x7f) { + // 0 NZA, so UTF-16BE + var newReader = new Utf16Reader(this.stream, true, this.errorThrow); + this.reader = newReader; + return c2; + } else { + var utf8reader = new Utf8Reader(this.stream, this.errorThrow); + utf8reader.Unget(c2); + this.reader = utf8reader; + return c1; + } + } + } + // Use default of UTF-8 + return -2; + } + + // Detects a Unicode encoding + private int DetectUnicodeEncoding() { + int mode = this.mode; + int c1 = this.stream.ReadByte(); + int c2; + if (c1 < 0) { + return -1; + } + Utf8Reader utf8reader; + if (mode == 0) { + // UTF-8 only + utf8reader = new Utf8Reader(this.stream, this.errorThrow); + this.reader = utf8reader; + c1 = utf8reader.ReadChar(); + if (c1 == 0xfeff) { + // Skip BOM + c1 = utf8reader.ReadChar(); + } + return c1; + } else if (mode == 1 || mode == 3) { + c2 = this.DetectUtf8OrUtf16(c1); + if (c2 >= -1) { + return c2; +} + } else if (mode == 2 || mode == 4) { + // UTF-8, UTF-16, or UTF-32 + c2 = this.DetectUtf8Or16Or32(c1); + if (c2 >= -1) { + return c2; +} + } + // Default case: assume UTF-8 + utf8reader = new Utf8Reader(this.stream, this.errorThrow); + this.reader = utf8reader; + utf8reader.Unget(c1); + c1 = utf8reader.ReadChar(); + if (!this.dontSkipUtf8Bom && c1 == 0xfeff) { + // Skip BOM + c1 = utf8reader.ReadChar(); + } + return c1; + } + + private sealed class SavedState { + private int[] saved; + private int savedLength; + + private void Ensure(int size) { + this.saved = this.saved ?? (new int[this.savedLength + size]); + if (this.savedLength + size < this.saved.Length) { + var newsaved = new int[this.savedLength + size + 4]; + Array.Copy(this.saved, 0, newsaved, 0, this.savedLength); + this.saved = newsaved; + } + } + + public void AddOne(int a) { + this.Ensure(1); + this.saved[this.savedLength++] = a; + } + + public void AddTwo(int a, int b) { + this.Ensure(2); + this.saved[this.savedLength + 1] = a; + this.saved[this.savedLength] = b; + this.savedLength += 2; + } + + public void AddThree(int a, int b, int c) { + this.Ensure(3); + this.saved[this.savedLength + 2] = a; + this.saved[this.savedLength + 1] = b; + this.saved[this.savedLength] = c; + this.savedLength += 3; + } + + public int Read(IByteReader input) { + if (this.savedLength > 0) { + int ret = this.saved[--this.savedLength]; + return ret; + } + return input.ReadByte(); + } + } + + private sealed class Utf16Reader : ICharacterInput { + private readonly bool bigEndian; + private readonly IByteReader stream; + private readonly SavedState state; + private readonly bool errorThrow; + + public Utf16Reader(IByteReader stream, bool bigEndian, bool errorThrow) { + this.stream = stream; + this.bigEndian = bigEndian; + this.state = new SavedState(); + this.errorThrow = errorThrow; + } + + public void Unget(int c1, int c2) { + this.state.AddTwo(c1, c2); + } + + public int ReadChar() { + int c1 = this.state.Read(this.stream); + if (c1 < 0) { + return -1; + } + int c2 = this.state.Read(this.stream); + if (c2 < 0) { + this.state.AddOne(-1); + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-16"); +} else { + return 0xfffd; +} + } + c1 = this.bigEndian ? ((c1 << 8) | c2) : ((c2 << 8) | c1); + int surr = c1 & 0xfc00; + if (surr == 0xd800) { + surr = c1; + c1 = this.state.Read(this.stream); + c2 = this.state.Read(this.stream); + if (c1 < 0 || c2 < 0) { + this.state.AddOne(-1); + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-16"); +} else { + return 0xfffd; +} + } + int unit2 = this.bigEndian ? ((c1 << 8) | c2) : ((c2 << 8) | c1); + if ((unit2 & 0xfc00) == 0xdc00) { + return 0x10000 + ((surr - 0xd800) << 10) + (unit2 - 0xdc00); + } + this.Unget(c1, c2); + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-16"); +} else { + return 0xfffd; +} + } + if (surr == 0xdc00) { + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-16"); +} else { + return 0xfffd; +} + } + return c1; + } + + public int Read(int[] chars, int index, int length) { + var count = 0; + for (int i = 0; i < length; ++i) { + int c = this.ReadChar(); + if (c < 0) { + return count; + } + chars[index + i] = c; + ++count; + } + return count; + } + } + + private sealed class Utf32Reader : ICharacterInput { + private readonly bool bigEndian; + private readonly IByteReader stream; + private readonly bool errorThrow; + private readonly SavedState state; + + public Utf32Reader(IByteReader stream, bool bigEndian, bool errorThrow) { + this.stream = stream; + this.bigEndian = bigEndian; + this.state = new SavedState(); + this.errorThrow = errorThrow; + } + + public int ReadChar() { + int c1 = this.state.Read(this.stream); + if (c1 < 0) { + return -1; + } + int c2 = this.state.Read(this.stream); + int c3 = this.state.Read(this.stream); + int c4 = this.state.Read(this.stream); + if (c2 < 0 || c3 < 0 || c4 < 0) { + this.state.AddOne(-1); + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-32"); +} else { + return 0xfffd; +} + } + c1 = this.bigEndian ? ((c1 << 24) | (c2 << 16) | (c3 << 8) | c4) : + ((c4 << 24) | (c3 << 16) | (c2 << 8) | c1); + if (c1 < 0 || c1 >= 0x110000 || (c1 & 0xfff800) == 0xd800) { + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-32"); +} else { + return 0xfffd; +} + } + return c1; + } + + public int Read(int[] chars, int index, int length) { + var count = 0; + for (int i = 0; i < length; ++i) { + int c = this.ReadChar(); + if (c < 0) { + return count; + } + chars[index + i] = c; + ++count; + } + return count; + } + } + + private sealed class Utf8Reader : ICharacterInput { + private readonly IByteReader stream; + private readonly SavedState state; + private readonly bool errorThrow; + private int lastChar; + + public Utf8Reader(IByteReader stream, bool errorThrow) { + this.stream = stream; + this.lastChar = -1; + this.state = new SavedState(); + this.errorThrow = errorThrow; + } + + public void Unget(int ch) { + this.state.AddOne(ch); + } + + public void UngetThree(int a, int b, int c) { + this.state.AddThree(a, b, c); + } + + public int ReadChar() { + var cp = 0; + var bytesSeen = 0; + var bytesNeeded = 0; + var lower = 0; + var upper = 0; + while (true) { + int b; + if (this.lastChar != -1) { + b = this.lastChar; + this.lastChar = -1; + } else { + b = this.state.Read(this.stream); + } + if (b < 0) { + if (bytesNeeded != 0) { + bytesNeeded = 0; + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-8"); +} else { + return 0xfffd; +} + } + return -1; + } + if (bytesNeeded == 0) { + if ((b & 0x7f) == b) { + return b; + } + if (b >= 0xc2 && b <= 0xdf) { + bytesNeeded = 1; + lower = 0x80; + upper = 0xbf; + cp = (b - 0xc0) << 6; + } else if (b >= 0xe0 && b <= 0xef) { + lower = (b == 0xe0) ? 0xa0 : 0x80; + upper = (b == 0xed) ? 0x9f : 0xbf; + bytesNeeded = 2; + cp = (b - 0xe0) << 12; + } else if (b >= 0xf0 && b <= 0xf4) { + lower = (b == 0xf0) ? 0x90 : 0x80; + upper = (b == 0xf4) ? 0x8f : 0xbf; + bytesNeeded = 3; + cp = (b - 0xf0) << 18; + } else { + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-8"); +} else { + return 0xfffd; +} + } + continue; + } + if (b < lower || b > upper) { + cp = bytesNeeded = bytesSeen = 0; + this.state.AddOne(b); + if (this.errorThrow) { + throw new InvalidOperationException("Invalid UTF-8"); +} else { + return 0xfffd; +} + } + lower = 0x80; + upper = 0xbf; + ++bytesSeen; + cp += (b - 0x80) << (6 * (bytesNeeded - bytesSeen)); + if (bytesSeen != bytesNeeded) { + continue; + } + int ret = cp; + cp = 0; + bytesSeen = 0; + bytesNeeded = 0; + return ret; + } + } + + public int Read(int[] chars, int index, int length) { + var count = 0; + for (int i = 0; i < length; ++i) { + int c = this.ReadChar(); + if (c < 0) { + return count; + } + chars[index + i] = c; + ++count; + } + return count; + } + } + + private sealed class WrappedStream : IByteReader { + private readonly Stream stream; + + public WrappedStream(Stream stream) { + this.stream = stream; + } + + public int ReadByte() { + try { + return this.stream.ReadByte(); + } catch (IOException ex) { + throw new InvalidOperationException(ex.Message, ex); + } + } + } + } +} diff --git a/PeterO/Cbor/FastInteger2.cs b/PeterO/Cbor/FastInteger2.cs new file mode 100644 index 00000000..45982932 --- /dev/null +++ b/PeterO/Cbor/FastInteger2.cs @@ -0,0 +1,602 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal sealed class FastInteger2 { + private sealed class MutableNumber { + private int[] data; + private int wordCount; + + internal MutableNumber(int val) { + if (val < 0) { + throw new ArgumentException("val (" + val + ") is less than " + "0 "); + } + this.data = new int[4]; + this.wordCount = (val == 0) ? 0 : 1; + this.data[0] = val; + } + + internal EInteger ToEInteger() { + if (this.wordCount == 1 && (this.data[0] >> 31) == 0) { + return (EInteger)((int)this.data[0]); + } + var bytes = new byte[(this.wordCount * 4) + 1]; + for (var i = 0; i < this.wordCount; ++i) { + bytes[i * 4] = (byte)(this.data[i] & 0xff); + bytes[(i * 4) + 1] = (byte)((this.data[i] >> 8) & 0xff); + bytes[(i * 4) + 2] = (byte)((this.data[i] >> 16) & 0xff); + bytes[(i * 4) + 3] = (byte)((this.data[i] >> 24) & 0xff); + } + bytes[bytes.Length - 1] = (byte)0; + return EInteger.FromBytes(bytes, true); + } + + internal int[] GetLastWordsInternal(int numWords32Bit) { + var ret = new int[numWords32Bit]; + Array.Copy(this.data, ret, Math.Min(numWords32Bit, this.wordCount)); + return ret; + } + + internal bool CanFitInInt32() { + return this.wordCount == 0 || (this.wordCount == 1 && (this.data[0] >> + 31) == 0); + } + + internal int ToInt32() { + return this.wordCount == 0 ? 0 : this.data[0]; + } + + internal MutableNumber Multiply(int multiplicand) { + if (multiplicand < 0) { + throw new ArgumentException("multiplicand (" + multiplicand + + ") is less than " + "0 "); + } + if (multiplicand != 0) { + var carry = 0; + if (this.wordCount == 0) { + if (this.data.Length == 0) { + this.data = new int[4]; + } + this.data[0] = 0; + this.wordCount = 1; + } + int result0, result1, result2, result3; + if (multiplicand < 65536) { + for (var i = 0; i < this.wordCount; ++i) { + int x0 = this.data[i]; + int x1 = x0; + int y0 = multiplicand; + x0 &= 65535; + x1 = (x1 >> 16) & 65535; + int temp = unchecked(x0 * y0); // a * c + result1 = (temp >> 16) & 65535; + result0 = temp & 65535; + result2 = 0; + temp = unchecked(x1 * y0); // b * c + result2 += (temp >> 16) & 65535; + result1 += temp & 65535; + result2 += (result1 >> 16) & 65535; + result1 &= 65535; + result3 = (result2 >> 16) & 65535; + result2 &= 65535; + // Add carry + x0 = unchecked((int)(result0 | (result1 << 16))); + x1 = unchecked((int)(result2 | (result3 << 16))); + int x2 = unchecked(x0 + carry); + if (((x2 >> 31) == (x0 >> 31)) ? ((x2 & Int32.MaxValue) < (x0 & + Int32.MaxValue)) : ((x2 >> 31) == 0)) { + // Carry in addition + x1 = unchecked(x1 + 1); + } + this.data[i] = x2; + carry = x1; + } + } else { + for (var i = 0; i < this.wordCount; ++i) { + int x0 = this.data[i]; + int x1 = x0; + int y0 = multiplicand; + int y1 = y0; + x0 &= 65535; + y0 &= 65535; + x1 = (x1 >> 16) & 65535; + y1 = (y1 >> 16) & 65535; + int temp = unchecked(x0 * y0); // a * c + result1 = (temp >> 16) & 65535; + result0 = temp & 65535; + temp = unchecked(x0 * y1); // a * d + result2 = (temp >> 16) & 65535; + result1 += temp & 65535; + result2 += (result1 >> 16) & 65535; + result1 &= 65535; + temp = unchecked(x1 * y0); // b * c + result2 += (temp >> 16) & 65535; + result1 += temp & 65535; + result2 += (result1 >> 16) & 65535; + result1 &= 65535; + result3 = (result2 >> 16) & 65535; + result2 &= 65535; + temp = unchecked(x1 * y1); // b * d + result3 += (temp >> 16) & 65535; + result2 += temp & 65535; + result3 += (result2 >> 16) & 65535; + result2 &= 65535; + // Add carry + x0 = unchecked((int)(result0 | (result1 << 16))); + x1 = unchecked((int)(result2 | (result3 << 16))); + int x2 = unchecked(x0 + carry); + if (((x2 >> 31) == (x0 >> 31)) ? ((x2 & Int32.MaxValue) < (x0 & + Int32.MaxValue)) : ((x2 >> 31) == 0)) { + // Carry in addition + x1 = unchecked(x1 + 1); + } + this.data[i] = x2; + carry = x1; + } + } + if (carry != 0) { + if (this.wordCount >= this.data.Length) { + var newdata = new int[this.wordCount + 20]; + Array.Copy(this.data, 0, newdata, 0, this.data.Length); + this.data = newdata; + } + this.data[this.wordCount] = carry; + ++this.wordCount; + } + // Calculate the correct data length + while (this.wordCount != 0 && this.data[this.wordCount - 1] == 0) { + --this.wordCount; + } + } else { + if (this.data.Length > 0) { + this.data[0] = 0; + } + this.wordCount = 0; + } + return this; + } + + internal int Sign { + get { + return this.wordCount == 0 ? 0 : 1; + } + } + + internal MutableNumber SubtractInt(int other) { + if (other < 0) { + throw new ArgumentException("other (" + other + ") is less than " + + "0 "); + } + if (other != 0) { + unchecked { + // Ensure a length of at least 1 + if (this.wordCount == 0) { + if (this.data.Length == 0) { + this.data = new int[4]; + } + this.data[0] = 0; + this.wordCount = 1; + } + int borrow; + int u; + int a = this.data[0]; + u = a - other; + borrow = ((((a >> 31) == (u >> 31)) ? + ((a & Int32.MaxValue) < (u & Int32.MaxValue)) : + ((a >> 31) == 0)) || (a == u && other != 0)) ? 1 : 0; + this.data[0] = (int)u; + if (borrow != 0) { + for (int i = 1; i < this.wordCount; ++i) { + u = this.data[i] - borrow; + borrow = (((this.data[i] >> 31) == (u >> 31)) ? + ((this.data[i] & Int32.MaxValue) < (u & Int32.MaxValue)) : + ((this.data[i] >> 31) == 0)) ? 1 : 0; + this.data[i] = (int)u; + } + } + // Calculate the correct data length + while (this.wordCount != 0 && this.data[this.wordCount - 1] == 0) { + --this.wordCount; + } + } + } + return this; + } + + internal MutableNumber Subtract(MutableNumber other) { + unchecked { + { + // Console.WriteLine("" + this.data.Length + " " + + // (other.data.Length)); + int neededSize = (this.wordCount > other.wordCount) ? + this.wordCount : other.wordCount; + if (this.data.Length < neededSize) { + var newdata = new int[neededSize + 20]; + Array.Copy(this.data, 0, newdata, 0, this.data.Length); + this.data = newdata; + } + neededSize = (this.wordCount < other.wordCount) ? this.wordCount : + other.wordCount; + var u = 0; + var borrow = 0; + for (var i = 0; i < neededSize; ++i) { + int a = this.data[i]; + u = (a - other.data[i]) - borrow; + borrow = ((((a >> 31) == (u >> 31)) ? ((a & Int32.MaxValue) < + (u & Int32.MaxValue)) : + ((a >> 31) == 0)) || (a == u && other.data[i] != + 0)) ? 1 : 0; + this.data[i] = (int)u; + } + if (borrow != 0) { + for (int i = neededSize; i < this.wordCount; ++i) { + int a = this.data[i]; + u = (a - other.data[i]) - borrow; + borrow = ((((a >> 31) == (u >> 31)) ? ((a & Int32.MaxValue) < + (u & Int32.MaxValue)) : + ((a >> 31) == 0)) || (a == u && other.data[i] != + 0)) ? 1 : 0; + this.data[i] = (int)u; + } + } + // Calculate the correct data length + while (this.wordCount != 0 && this.data[this.wordCount - 1] == 0) { + --this.wordCount; + } + return this; + } + } + } + + internal MutableNumber Add(int augend) { + if (augend < 0) { + throw new ArgumentException("augend (" + augend + ") is less than " + + "0 "); + } + unchecked { + if (augend != 0) { + var carry = 0; + // Ensure a length of at least 1 + if (this.wordCount == 0) { + if (this.data.Length == 0) { + this.data = new int[4]; + } + this.data[0] = 0; + this.wordCount = 1; + } + for (var i = 0; i < this.wordCount; ++i) { + int u; + int a = this.data[i]; + u = (a + augend) + carry; + carry = ((((u >> 31) == (a >> 31)) ? ((u & Int32.MaxValue) < (a & + Int32.MaxValue)) : + ((u >> 31) == 0)) || (u == a && augend != 0)) ? 1 : 0; + this.data[i] = u; + if (carry == 0) { + return this; + } + augend = 0; + } + if (carry != 0) { + if (this.wordCount >= this.data.Length) { + var newdata = new int[this.wordCount + 20]; + Array.Copy(this.data, 0, newdata, 0, this.data.Length); + this.data = newdata; + } + this.data[this.wordCount] = carry; + ++this.wordCount; + } + } + // Calculate the correct data length + while (this.wordCount != 0 && this.data[this.wordCount - 1] == 0) { + --this.wordCount; + } + return this; + } + } + } + + private int smallValue; // if integerMode is 0 + private MutableNumber mnum; // if integerMode is 1 + private EInteger largeValue; // if integerMode is 2 + private int integerMode; + + internal FastInteger2(int value) { + this.smallValue = value; + } + + internal int AsInt32() { + switch (this.integerMode) { + case 0: + return this.smallValue; + case 1: + return this.mnum.ToInt32(); + case 2: + return (int)this.largeValue; + default: throw new InvalidOperationException(); + } + } + + internal static EInteger WordsToEInteger(int[] words) { + int wordCount = words.Length; + if (wordCount == 1 && (words[0] >> 31) == 0) { + return (EInteger)((int)words[0]); + } + var bytes = new byte[(wordCount * 4) + 1]; + for (var i = 0; i < wordCount; ++i) { + bytes[(i * 4) + 0] = (byte)(words[i] & 0xff); + bytes[(i * 4) + 1] = (byte)((words[i] >> 8) & 0xff); + bytes[(i * 4) + 2] = (byte)((words[i] >> 16) & 0xff); + bytes[(i * 4) + 3] = (byte)((words[i] >> 24) & 0xff); + } + bytes[bytes.Length - 1] = (byte)0; + return EInteger.FromBytes(bytes, true); + } + + internal FastInteger2 SetInt(int val) { + this.smallValue = val; + this.integerMode = 0; + return this; + } + + /// + internal FastInteger2 Multiply(int val) { + if (val == 0) { + this.smallValue = 0; + this.integerMode = 0; + } else { + switch (this.integerMode) { + case 0: + bool apos = this.smallValue > 0L; + bool bpos = val > 0L; + if ( + (apos && ((!bpos && (Int32.MinValue / this.smallValue) > val) || + (bpos && this.smallValue > (Int32.MaxValue / val)))) || + (!apos && ((!bpos && this.smallValue != 0L && + (Int32.MaxValue / this.smallValue) > val) || + (bpos && this.smallValue < (Int32.MinValue / val))))) { + // would overflow, convert to large + if (apos && bpos) { + // if both operands are nonnegative + // convert to mutable big integer + this.integerMode = 1; + this.mnum = new MutableNumber(this.smallValue); + this.mnum.Multiply(val); + } else { + // if either operand is negative + // convert to big integer + this.integerMode = 2; + this.largeValue = (EInteger)this.smallValue; + this.largeValue *= (EInteger)val; + } + } else { + smallValue *= val; + } + break; + case 1: + if (val < 0) { + this.integerMode = 2; + this.largeValue = this.mnum.ToEInteger(); + this.largeValue *= (EInteger)val; + } else { + mnum.Multiply(val); + } + break; + case 2: + this.largeValue *= (EInteger)val; + break; + default: throw new InvalidOperationException(); + } + } + return this; + } + + /// + internal FastInteger2 Subtract(FastInteger2 val) { + EInteger valValue; + switch (this.integerMode) { + case 0: + if (val.integerMode == 0) { + int vsv = val.smallValue; + if ((vsv < 0 && Int32.MaxValue + vsv < this.smallValue) || + (vsv > 0 && Int32.MinValue + vsv > this.smallValue)) { + // would overflow, convert to large + this.integerMode = 2; + this.largeValue = (EInteger)this.smallValue; + this.largeValue -= (EInteger)vsv; + } else { + this.smallValue -= vsv; + } + } else { + integerMode = 2; + largeValue = (EInteger)smallValue; + valValue = val.AsBigInteger(); + largeValue -= (EInteger)valValue; + } + break; + case 1: + if (val.integerMode == 1) { + // NOTE: Mutable numbers are + // currently always zero or positive + this.mnum.Subtract(val.mnum); + } else if (val.integerMode == 0 && val.smallValue >= 0) { + mnum.SubtractInt(val.smallValue); + } else { + integerMode = 2; + largeValue = mnum.ToEInteger(); + valValue = val.AsBigInteger(); + largeValue -= (EInteger)valValue; + } + break; + case 2: + valValue = val.AsBigInteger(); + this.largeValue -= (EInteger)valValue; + break; + default: throw new InvalidOperationException(); + } + return this; + } + + /// + internal FastInteger2 SubtractInt(int val) { + if (val == Int32.MinValue) { + return this.AddInt(Int32.MaxValue).AddInt(1); + } + if (this.integerMode == 0) { + if ((val < 0 && Int32.MaxValue + val < this.smallValue) || + (val > 0 && Int32.MinValue + val > this.smallValue)) { + // would overflow, convert to large + this.integerMode = 2; + this.largeValue = (EInteger)this.smallValue; + this.largeValue -= (EInteger)val; + } else { + this.smallValue -= val; + } + return this; + } + return this.AddInt(-val); + } + + internal FastInteger2 Add(FastInteger2 val) { + EInteger valValue; + switch (this.integerMode) { + case 0: + if (val.integerMode == 0) { + if ((this.smallValue < 0 && (int)val.smallValue < Int32.MinValue + - this.smallValue) || + (this.smallValue > 0 && (int)val.smallValue > Int32.MaxValue + - this.smallValue)) { + // would overflow + if (val.smallValue >= 0) { + this.integerMode = 1; + this.mnum = new MutableNumber(this.smallValue); + this.mnum.Add(val.smallValue); + } else { + this.integerMode = 2; + this.largeValue = (EInteger)this.smallValue; + this.largeValue += (EInteger)val.smallValue; + } + } else { + this.smallValue += val.smallValue; + } + } else { + integerMode = 2; + largeValue = (EInteger)smallValue; + valValue = val.AsBigInteger(); + largeValue += (EInteger)valValue; + } + break; + case 1: + if (val.integerMode == 0 && val.smallValue >= 0) { + this.mnum.Add(val.smallValue); + } else { + integerMode = 2; + largeValue = mnum.ToEInteger(); + valValue = val.AsBigInteger(); + largeValue += (EInteger)valValue; + } + break; + case 2: + valValue = val.AsBigInteger(); + this.largeValue += (EInteger)valValue; + break; + default: throw new InvalidOperationException(); + } + return this; + } + + internal FastInteger2 AddInt(int val) { + EInteger valValue; + switch (this.integerMode) { + case 0: + if ((this.smallValue < 0 && (int)val < Int32.MinValue - + this.smallValue) || (this.smallValue > 0 && (int)val > + Int32.MaxValue - this.smallValue)) { + // would overflow + if (val >= 0) { + this.integerMode = 1; + this.mnum = new MutableNumber(this.smallValue); + this.mnum.Add(val); + } else { + this.integerMode = 2; + this.largeValue = (EInteger)this.smallValue; + this.largeValue += (EInteger)val; + } + } else { + smallValue += val; + } + break; + case 1: + if (val >= 0) { + this.mnum.Add(val); + } else { + integerMode = 2; + largeValue = mnum.ToEInteger(); + valValue = (EInteger)val; + largeValue += (EInteger)valValue; + } + break; + case 2: + valValue = (EInteger)val; + this.largeValue += (EInteger)valValue; + break; + default: throw new InvalidOperationException(); + } + return this; + } + + internal bool CanFitInInt32() { + switch (this.integerMode) { + case 0: + return true; + case 1: + return this.mnum.CanFitInInt32(); + case 2: { + return this.largeValue.CanFitInInt32(); + } + default: + throw new InvalidOperationException(); + } + } + + /// + internal int Sign { + get { + switch (this.integerMode) { + case 0: + return (this.smallValue == 0) ? 0 : ((this.smallValue < 0) ? -1 : + 1); + case 1: + return this.mnum.Sign; + case 2: + return this.largeValue.Sign; + default: return 0; + } + } + } + + internal EInteger AsBigInteger() { + switch (this.integerMode) { + case 0: + return EInteger.FromInt32(this.smallValue); + case 1: + return this.mnum.ToEInteger(); + case 2: + return this.largeValue; + default: throw new InvalidOperationException(); + } + } + } +} diff --git a/PeterO/Cbor/ICBORConverter.cs b/PeterO/Cbor/ICBORConverter.cs new file mode 100644 index 00000000..8bd72958 --- /dev/null +++ b/PeterO/Cbor/ICBORConverter.cs @@ -0,0 +1,19 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + /// + public interface ICBORConverter + { + /// + CBORObject ToCBORObject(T obj); + } +} diff --git a/PeterO/Cbor/ICBORNumber.cs b/PeterO/Cbor/ICBORNumber.cs new file mode 100644 index 00000000..43352fd0 --- /dev/null +++ b/PeterO/Cbor/ICBORNumber.cs @@ -0,0 +1,63 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal interface ICBORNumber + { + bool IsPositiveInfinity(Object obj); + + bool IsInfinity(Object obj); + + bool IsNegativeInfinity(Object obj); + + bool IsNaN(Object obj); + + bool IsNegative(Object obj); + + double AsDouble(Object obj); + + object Negate(Object obj); + + object Abs(Object obj); + + EDecimal AsExtendedDecimal(Object obj); + + EFloat AsExtendedFloat(Object obj); + + ERational AsExtendedRational(Object obj); + + float AsSingle(Object obj); + + EInteger AsEInteger(Object obj); + + long AsInt64(Object obj); + + bool CanFitInSingle(Object obj); + + bool CanFitInDouble(Object obj); + + bool CanFitInInt32(Object obj); + + bool CanFitInInt64(Object obj); + + bool CanTruncatedIntFitInInt64(Object obj); + + bool CanTruncatedIntFitInInt32(Object obj); + + int AsInt32(Object obj, int minValue, int maxValue); + + bool IsZero(Object obj); + + int Sign(Object obj); + + bool IsIntegral(Object obj); + } +} diff --git a/PeterO/Cbor/ICBORTag.cs b/PeterO/Cbor/ICBORTag.cs new file mode 100644 index 00000000..c19c829d --- /dev/null +++ b/PeterO/Cbor/ICBORTag.cs @@ -0,0 +1,24 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; + +namespace PeterO.Cbor { + /// + [Obsolete("May be removed in the future without replacement. Not as useful as ICBORConverters and ICBORObjectConverters for FromObject and ToObject.")] + public interface ICBORTag + { + /// + CBORTypeFilter GetTypeFilter(); + + /// + CBORObject ValidateObject(CBORObject obj); + } +} diff --git a/PeterO/Cbor/ICBORToFromConverter.cs b/PeterO/Cbor/ICBORToFromConverter.cs new file mode 100644 index 00000000..2037d88e --- /dev/null +++ b/PeterO/Cbor/ICBORToFromConverter.cs @@ -0,0 +1,11 @@ +using System; + +namespace PeterO.Cbor { + /// + public interface ICBORToFromConverter : ICBORConverter { + /// + T FromCBORObject(CBORObject cbor); + } +} diff --git a/PeterO/Cbor/ICharacterInput.cs b/PeterO/Cbor/ICharacterInput.cs new file mode 100644 index 00000000..3d4a4c63 --- /dev/null +++ b/PeterO/Cbor/ICharacterInput.cs @@ -0,0 +1,15 @@ +using System; + +namespace PeterO.Cbor { + /// + internal interface ICharacterInput { + /// + int ReadChar(); + + /// + int Read(int[] chars, int index, int length); + } +} diff --git a/PeterO/Cbor/JSONOptions.cs b/PeterO/Cbor/JSONOptions.cs new file mode 100644 index 00000000..c6c0bb6a --- /dev/null +++ b/PeterO/Cbor/JSONOptions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PeterO.Cbor { + /// + public sealed class JSONOptions { + /// + public JSONOptions() : this(false) { +} + + /// + public JSONOptions(bool base64Padding) { + this.Base64Padding = base64Padding; + } + + /// + public static readonly JSONOptions Default = new JSONOptions(); + + /// + public bool Base64Padding { get; private set; } + } +} diff --git a/PeterO/Cbor/PODOptions.cs b/PeterO/Cbor/PODOptions.cs new file mode 100644 index 00000000..be6d88d1 --- /dev/null +++ b/PeterO/Cbor/PODOptions.cs @@ -0,0 +1,34 @@ +using System; + +namespace PeterO.Cbor { + /// + public class PODOptions { + /// + public PODOptions() : this(true, true) { +} + + /// + public PODOptions(bool removeIsPrefix, bool useCamelCase) { + #pragma warning disable 618 + this.RemoveIsPrefix = removeIsPrefix; + #pragma warning restore 618 + this.UseCamelCase = useCamelCase; + } + + /// + public static readonly PODOptions Default = new PODOptions(); + + /// + [Obsolete("Property name conversion may change, making this property obsolete.")] + public bool RemoveIsPrefix { get; private set; } + + /// + public bool UseCamelCase { get; private set; } + } +} diff --git a/PeterO/Cbor/PropertyMap.cs b/PeterO/Cbor/PropertyMap.cs new file mode 100644 index 00000000..28f087a1 --- /dev/null +++ b/PeterO/Cbor/PropertyMap.cs @@ -0,0 +1,628 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal static class PropertyMap { +// TODO: Remove in next major version +internal const bool DateTimeCompatHack = true; + + private sealed class PropertyData { + private string name; + + public string Name { + get { + return this.name; + } + + set { + this.name = value; + } + } + + private PropertyInfo prop; + + public string GetAdjustedName(bool removeIsPrefix, bool useCamelCase) { + string thisName = this.Name; + // Convert 'IsXYZ' to 'XYZ' + if (removeIsPrefix && thisName.Length >= 3 && thisName[0] == 'I' && + thisName[1] == 's' && thisName[2] >= 'A' && thisName[2] <= 'Z') { + // NOTE (Jun. 17, 2017, Peter O.): Was "== 'Z'", which was a + // bug reported + // by GitHub user "richardschneider". See peteroupc/CBOR#17. + thisName = thisName.Substring(2); + } + // Convert to camel case + if (useCamelCase && thisName[0] >= 'A' && thisName[0] <= 'Z') { + var sb = new System.Text.StringBuilder(); + sb.Append((char)(thisName[0] + 0x20)); + sb.Append(thisName.Substring(1)); + thisName = sb.ToString(); + } + return thisName; + } + + public PropertyInfo Prop { + get { + return this.prop; + } + + set { + this.prop = value; + } + } + } + +#if NET40 || NET20 + private static IEnumerable GetTypeProperties(Type t) { + return t.GetProperties(BindingFlags.Public | + BindingFlags.Instance); + } + + private static MethodInfo GetTypeMethod( + Type t, + string name, + Type[] parameters) { + return t.GetMethod(name, parameters); + } + + private static bool HasCustomAttribute( + Type t, + string name) { +#if NET40 || NET20 + foreach (var attr in t.GetCustomAttributes(false)) { +#else + foreach (var attr in t.CustomAttributes) { +#endif + if (attr.GetType().FullName.Equals(name)) { + return true; + } + } + return false; + } +#else + private static IEnumerable GetTypeProperties(Type t) { + return t.GetRuntimeProperties(); + } + + private static MethodInfo GetTypeMethod( + Type t, + string name, + Type[] parameters) { + return t.GetRuntimeMethod(name, parameters); + } + + private static bool HasCustomAttribute( + Type t, + string name) { + foreach (var attr in t.GetTypeInfo().GetCustomAttributes()) { + if (attr.GetType().FullName.Equals(name)) { + return true; + } + } + return false; + } + +#endif + + private static readonly IDictionary> + ValuePropertyLists = new Dictionary>(); + + private static IList GetPropertyList(Type t) { + lock (ValuePropertyLists) { + if ( + ValuePropertyLists.TryGetValue( + t, + out IList ret)) { + return ret; + } + ret = new List(); + bool anonymous = HasCustomAttribute( + t, + "System.Runtime.CompilerServices.CompilerGeneratedAttribute"); + foreach (PropertyInfo pi in GetTypeProperties(t)) { + if (pi.CanRead && (pi.CanWrite || anonymous) && + pi.GetIndexParameters().Length == 0) { + PropertyData pd = new PropertyMap.PropertyData() { + Name = pi.Name, + Prop = pi + }; + ret.Add(pd); + } + } + ValuePropertyLists.Add(t, ret); + return ret; + } + } + + public static bool ExceedsKnownLength(Stream inStream, long size) { + return (inStream is MemoryStream) && (size > (inStream.Length - + inStream.Position)); + } + + public static void SkipStreamToEnd(Stream inStream) { + if (inStream is MemoryStream) { + inStream.Position = inStream.Length; + } + } + + // Inappropriate to mark these obsolete; they're + // just non-publicly-visible methods to convert to + // and from legacy arbitrary-precision classes +#pragma warning disable 618 + public static BigInteger ToLegacy(EInteger ei) { + return BigInteger.ToLegacy(ei); + } + + public static ExtendedDecimal ToLegacy(EDecimal ed) { + return ExtendedDecimal.ToLegacy(ed); + } + + public static ExtendedFloat ToLegacy(EFloat ef) { + return ExtendedFloat.ToLegacy(ef); + } + + public static ExtendedRational ToLegacy(ERational er) { + return ExtendedRational.ToLegacy(er); + } + + public static EInteger FromLegacy(BigInteger ei) { + return BigInteger.FromLegacy(ei); + } + + public static EDecimal FromLegacy(ExtendedDecimal ed) { + return ExtendedDecimal.FromLegacy(ed); + } + + public static EFloat FromLegacy(ExtendedFloat ef) { + return ExtendedFloat.FromLegacy(ef); + } + + public static ERational FromLegacy(ExtendedRational er) { + return ExtendedRational.FromLegacy(er); + } +#pragma warning restore 618 + public static bool FirstElement(int[] index, int[] dimensions) { + foreach (var d in dimensions) { + if (d == 0) { + return false; +} + } + return true; + } + + public static bool NextElement(int[] index, int[] dimensions) { + for (var i = dimensions.Length - 1; i >= 0; --i) { + if (dimensions[i] > 0) { + ++index[i]; + if (index[i] >= dimensions[i]) { + index[i] = 0; + } else { + return true; +} + } + } + return false; + } + + public static CBORObject BuildCBORArray(int[] dimensions) { + int zeroPos = dimensions.Length; + for (var i = 0; i < dimensions.Length; ++i) { + if (dimensions[i] == 0) { + {zeroPos = i; +} break; +} + } + int arraydims = zeroPos - 1; + if (arraydims <= 0) { + return CBORObject.NewArray(); +} + var stack = new CBORObject[zeroPos]; + var index = new int[zeroPos]; + var stackpos = 0; + CBORObject ret = CBORObject.NewArray(); + stack[0] = ret; + index[0] = 0; + for (var i = 0; i < dimensions[0]; ++i) { + ret.Add(CBORObject.NewArray()); + } + ++stackpos; + while (stackpos > 0) { + int curindex = index[stackpos - 1]; + if (curindex < stack[stackpos - 1].Count) { + CBORObject subobj = stack[stackpos - 1][curindex]; + if (stackpos < zeroPos) { + stack[stackpos] = subobj; + index[stackpos] = 0; + for (var i = 0; i < dimensions[stackpos]; ++i) { + subobj.Add(CBORObject.NewArray()); + } + ++index[stackpos - 1]; + ++stackpos; + } else { + ++index[stackpos - 1]; + } + } else { + --stackpos; + } + } + return ret; + } + + public static CBORObject FromArray( + Object arrObj, + PODOptions options) { + var arr = (Array)arrObj; + int rank = arr.Rank; + if (rank == 0) { + return CBORObject.NewArray(); + } + CBORObject obj = null; + if (rank == 1) { + // Most common case: the array is one-dimensional + obj = CBORObject.NewArray(); + int len = arr.GetLength(0); + for (var i = 0; i < len; ++i) { + obj.Add( + CBORObject.FromObject( + arr.GetValue(i), + options)); + } + return obj; + } + var index = new int[rank]; + var dimensions = new int[rank]; + for (var i = 0; i < rank; ++i) { dimensions[i] = arr.GetLength(i); +} + if (!FirstElement(index, dimensions)) { + return obj; +} + obj = BuildCBORArray(dimensions); + do { + CBORObject o = CBORObject.FromObject( + arr.GetValue(index), + options); + SetCBORObject(obj, index, o); + } while (NextElement(index, dimensions)); + return obj; + } + + private static CBORObject GetCBORObject(CBORObject cbor, int[] index) { + CBORObject ret = cbor; + foreach (var i in index) { ret = ret[i]; +} + return ret; + } + + private static void SetCBORObject( + CBORObject cbor, + int[] index, + CBORObject obj) { + CBORObject ret = cbor; + for (var i = 0; i < index.Length - 1; ++i) { + ret = ret[index[i]]; + } + int ilen = index[index.Length - 1]; + while (ilen >= ret.Count) { + { ret.Add(CBORObject.Null); +} +} + // TODO: Make Set(object, object) work with arrays + // in next major version + ret[ilen] = obj; + } + + public static object EnumToObject(Enum value) { + Type t = Enum.GetUnderlyingType(value.GetType()); + if (t.Equals(typeof(ulong))) { + var data = new byte[13]; + ulong uvalue = Convert.ToUInt64(value); + data[0] = (byte)(uvalue & 0xff); + data[1] = (byte)((uvalue >> 8) & 0xff); + data[2] = (byte)((uvalue >> 16) & 0xff); + data[3] = (byte)((uvalue >> 24) & 0xff); + data[4] = (byte)((uvalue >> 32) & 0xff); + data[5] = (byte)((uvalue >> 40) & 0xff); + data[6] = (byte)((uvalue >> 48) & 0xff); + data[7] = (byte)((uvalue >> 56) & 0xff); + data[8] = (byte)0; + return EInteger.FromBytes(data, true); + } + return t.Equals(typeof(long)) ? Convert.ToInt64(value) : + (t.Equals(typeof(uint)) ? Convert.ToInt64(value) : + Convert.ToInt32(value)); + } + + public static object FindOneArgumentMethod( + object obj, + string name, + Type argtype) { + return GetTypeMethod(obj.GetType(), name, new[] { argtype }); + } + + public static object InvokeOneArgumentMethod( + object methodInfo, + object obj, + object argument) { + return ((MethodInfo)methodInfo).Invoke(obj, new[] { argument }); + } + + public static byte[] UUIDToBytes(Guid guid) { + var bytes2 = new byte[16]; + var bytes = guid.ToByteArray(); + Array.Copy(bytes, bytes2, 16); + // Swap the bytes to conform with the UUID RFC + bytes2[0] = bytes[3]; + bytes2[1] = bytes[2]; + bytes2[2] = bytes[1]; + bytes2[3] = bytes[0]; + bytes2[4] = bytes[5]; + bytes2[5] = bytes[4]; + bytes2[6] = bytes[7]; + bytes2[7] = bytes[6]; + return bytes2; + } + + private static bool StartsWith(string str, string pfx) { +return str != null && str.Length >= pfx.Length && + str.Substring(0, pfx.Length).Equals(pfx); + } + + public static object TypeToObject(CBORObject objThis, Type t) { + if (t.Equals(typeof(DateTime))) { + return new CBORTag0().FromCBORObject(objThis); + } + if (t.Equals(typeof(Guid))) { + return new CBORTag37().FromCBORObject(objThis); + } + if (t.Equals(typeof(int))) { + return objThis.AsInt32(); + } + if (t.Equals(typeof(long))) { + return objThis.AsInt64(); + } + if (t.Equals(typeof(double))) { + return objThis.AsDouble(); + } + if (t.Equals(typeof(float))) { + return objThis.AsSingle(); + } + if (t.Equals(typeof(bool))) { + return objThis.IsTrue; + } + +if (t.FullName != null && + (StartsWith(t.FullName, "System.Win32.") || + StartsWith(t.FullName, "System.IO."))) { + throw new NotSupportedException("Type " + t.FullName + " not supported"); +} + + if (objThis.Type == CBORType.ByteString) { + if (t.Equals(typeof(byte[]))) { + byte[] bytes = objThis.GetByteString(); + var byteret = new byte[bytes.Length]; + Array.Copy(bytes, 0, byteret, 0, byteret.Length); + return byteret; + } + } + if (objThis.Type == CBORType.Array) { + Type objectType = typeof(object); + var isList = false; + object listObject = null; +#if NET40 || NET20 + if (t.IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isList = td.Equals(typeof(List<>)) || td.Equals(typeof(IList<>)) || + td.Equals(typeof(ICollection<>)) || + td.Equals(typeof(IEnumerable<>)); + } else { + throw new NotImplementedException(); + } + isList = isList && t.GetGenericArguments().Length == 1; + if (isList) { + objectType = t.GetGenericArguments()[0]; + Type listType = typeof(List<>).MakeGenericType(objectType); + listObject = Activator.CreateInstance(listType); + } +#else + if (t.GetTypeInfo().IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isList = (td.Equals(typeof(List<>)) || + td.Equals(typeof(IList<>)) || + td.Equals(typeof(ICollection<>)) || + td.Equals(typeof(IEnumerable<>))); + } else { + throw new NotImplementedException(); + } + isList = (isList && t.GenericTypeArguments.Length == 1); + if (isList) { + objectType = t.GenericTypeArguments[0]; + Type listType = typeof(List<>).MakeGenericType(objectType); + listObject = Activator.CreateInstance(listType); + } +#endif + if (listObject != null) { + System.Collections.IList ie = (System.Collections.IList)listObject; + foreach (CBORObject value in objThis.Values) { + ie.Add(value.ToObject(objectType)); + } + return listObject; + } + } + if (objThis.Type == CBORType.Map) { + var isDict = false; + Type keyType = null; + Type valueType = null; + object dictObject = null; +#if NET40 || NET20 + isDict = t.IsGenericType; + if (t.IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isDict = td.Equals(typeof(Dictionary<,>)) || + td.Equals(typeof(IDictionary<,>)); + } + // DebugUtility.Log("list=" + isDict); + isDict = isDict && t.GetGenericArguments().Length == 2; + // DebugUtility.Log("list=" + isDict); + if (isDict) { + keyType = t.GetGenericArguments()[0]; + valueType = t.GetGenericArguments()[1]; + Type listType = typeof(Dictionary<,>).MakeGenericType( + keyType, + valueType); + dictObject = Activator.CreateInstance(listType); + } +#else + isDict = (t.GetTypeInfo().IsGenericType); + if (t.GetTypeInfo().IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isDict = (td.Equals(typeof(Dictionary<,>)) || + td.Equals(typeof(IDictionary<,>))); + } + //DebugUtility.Log("list=" + isDict); + isDict = (isDict && t.GenericTypeArguments.Length == 2); + //DebugUtility.Log("list=" + isDict); + if (isDict) { + keyType = t.GenericTypeArguments[0]; + valueType = t.GenericTypeArguments[1]; + Type listType = typeof(Dictionary<,>).MakeGenericType( + keyType, + valueType); + dictObject = Activator.CreateInstance(listType); + } +#endif + if (dictObject != null) { + System.Collections.IDictionary idic = + (System.Collections.IDictionary)dictObject; + foreach (CBORObject key in objThis.Keys) { + CBORObject value = objThis[key]; + idic.Add( + key.ToObject(keyType), + value.ToObject(valueType)); + } + return dictObject; + } + var values = new List>(); + foreach (string key in PropertyMap.GetPropertyNames( + t, + true, + true)) { + if (objThis.ContainsKey(key)) { + CBORObject cborValue = objThis[key]; + var dict = new KeyValuePair( + key, + cborValue); + values.Add(dict); + } + } + return PropertyMap.ObjectWithProperties( + t, + values, + true, + true); + } else { + throw new NotSupportedException(); + } + } + + public static object ObjectWithProperties( + Type t, + IEnumerable> keysValues) { + return ObjectWithProperties(t, keysValues, true, true); + } + + public static object ObjectWithProperties( + Type t, + IEnumerable> keysValues, + bool removeIsPrefix, + bool useCamelCase) { + object o = null; +#if NET20 || NET40 + foreach (var ci in t.GetConstructors()) { +#else + foreach (var ci in t.GetTypeInfo().DeclaredConstructors) { +#endif + if (ci.IsPublic) { + int nump = ci.GetParameters().Length; + o = ci.Invoke(new object[nump]); + break; + } + } + o = o ?? Activator.CreateInstance(t); + var dict = new Dictionary(); + foreach (var kv in keysValues) { + var name = kv.Key; + dict[name] = kv.Value; + } + foreach (PropertyData key in GetPropertyList(o.GetType())) { + var name = key.GetAdjustedName(removeIsPrefix, useCamelCase); + if (dict.ContainsKey(name)) { + object dobj = dict[name].ToObject(key.Prop.PropertyType); + key.Prop.SetValue(o, dobj, null); + } + } + return o; + } + + public static IEnumerable> + GetProperties(Object o) { + return GetProperties(o, true, true); + } + + public static IEnumerable + GetPropertyNames(Type t, bool removeIsPrefix, bool useCamelCase) { + foreach (PropertyData key in GetPropertyList(t)) { + yield return key.GetAdjustedName(removeIsPrefix, useCamelCase); + } + } + + public static IEnumerable> + GetProperties(Object o, bool removeIsPrefix, bool useCamelCase) { + foreach (PropertyData key in GetPropertyList(o.GetType())) { + yield return new KeyValuePair( + key.GetAdjustedName(removeIsPrefix, useCamelCase), + key.Prop.GetValue(o, null)); + } + } + + public static void BreakDownDateTime( + DateTime bi, + EInteger[] year, + int[] lf) { +#if NET20 + DateTime dt = bi.ToUniversalTime(); +#else + DateTime dt = TimeZoneInfo.ConvertTime(bi, TimeZoneInfo.Utc); +#endif + year[0] = EInteger.FromInt32(dt.Year); + lf[0] = dt.Month; + lf[1] = dt.Day; + lf[2] = dt.Hour; + lf[3] = dt.Minute; + lf[4] = dt.Second; + // lf[5] is the number of nanoseconds + lf[5] = (int)(dt.Ticks % 10000000L) * 100; + } + + public static DateTime BuildUpDateTime(EInteger year, int[] dt) { + return new DateTime( + year.ToInt32Checked(), + dt[0], + dt[1], + dt[2], + dt[3], + dt[4], + DateTimeKind.Utc).AddMinutes(-dt[6]).AddTicks((long)(dt[5] / 100)); + } + } +} diff --git a/PeterO/Cbor/PropertyMap2.cs b/PeterO/Cbor/PropertyMap2.cs new file mode 100644 index 00000000..c22dd23f --- /dev/null +++ b/PeterO/Cbor/PropertyMap2.cs @@ -0,0 +1,808 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal static class PropertyMap2 { + private sealed class PropertyData { + private string name; + + public string Name { + get { + return this.name; + } + + set { + this.name = value; + } + } + + private PropertyInfo prop; +#if NET20 || NET40 + public static bool HasUsableGetter(PropertyInfo pi) { + return pi != null && pi.CanRead && !pi.GetGetMethod().IsStatic && + pi.GetGetMethod().IsPublic; + } + + public static bool HasUsableSetter(PropertyInfo pi) { + return pi != null && pi.CanWrite && !pi.GetSetMethod().IsStatic && + pi.GetSetMethod().IsPublic; + } +#else + public static bool HasUsableGetter(PropertyInfo pi) { + return pi != null && pi.CanRead && !pi.GetMethod.IsStatic && + pi.GetMethod.IsPublic; + } + + public static bool HasUsableSetter(PropertyInfo pi) { + return pi != null && pi.CanWrite && !pi.SetMethod.IsStatic && + pi.SetMethod.IsPublic; + } +#endif + public bool HasUsableGetter() { + return HasUsableGetter(this.prop); + } + + public bool HasUsableSetter() { + return HasUsableSetter(this.prop); + } + + public string GetAdjustedName(bool useCamelCase) { + string thisName = this.Name; + if (useCamelCase) { + if (CBORUtilities.NameStartsWithWord(thisName, "Is")) { + thisName = thisName.Substring(2); + } + thisName = CBORUtilities.FirstCharLower(thisName); + } else { + thisName = CBORUtilities.FirstCharUpper(thisName); + } + return thisName; + } + + public PropertyInfo Prop { + get { + return this.prop; + } + + set { + this.prop = value; + } + } + } + +#if NET40 || NET20 + private static IEnumerable GetTypeProperties(Type t) { + return t.GetProperties(BindingFlags.Public | + BindingFlags.Instance); + } + + private static bool IsAssignableFrom(Type superType, Type subType) { + return superType.IsAssignableFrom(subType); + } + + private static MethodInfo GetTypeMethod( + Type t, + string name, + Type[] parameters) { + return t.GetMethod(name, parameters); + } + + private static bool HasCustomAttribute( + Type t, + string name) { +#if NET40 || NET20 + foreach (var attr in t.GetCustomAttributes(false)) { +#else + foreach (var attr in t.CustomAttributes) { +#endif + if (attr.GetType().FullName.Equals(name)) { + return true; + } + } + return false; + } +#else + private static bool IsAssignableFrom(Type superType, Type subType) { + return superType.GetTypeInfo().IsAssignableFrom(subType.GetTypeInfo()); + } + + private static IEnumerable GetTypeProperties(Type t) { + return t.GetRuntimeProperties(); + } + + private static MethodInfo GetTypeMethod( + Type t, + string name, + Type[] parameters) { + return t.GetRuntimeMethod(name, parameters); + } + + private static bool HasCustomAttribute( + Type t, + string name) { + foreach (var attr in t.GetTypeInfo().GetCustomAttributes()) { + if (attr.GetType().FullName.Equals(name)) { + return true; + } + } + return false; + } + +#endif + + private static readonly IDictionary> + ValuePropertyLists = new Dictionary>(); + + private static string RemoveIsPrefix(string pn) { + return CBORUtilities.NameStartsWithWord(pn, "Is") ? pn.Substring(2) : pn; + } + + private static IList GetPropertyList(Type t) { + lock (ValuePropertyLists) { + if ( + ValuePropertyLists.TryGetValue( + t, + out IList ret)) { + return ret; + } + ret = new List(); + bool anonymous = HasCustomAttribute( + t, + "System.Runtime.CompilerServices.CompilerGeneratedAttribute"); + var names = new Dictionary(); + foreach (PropertyInfo pi in GetTypeProperties(t)) { + var pn = RemoveIsPrefix(pi.Name); + if (names.ContainsKey(pn)) { + ++names[pn]; + } else { + names[pn] = 1; + } + } + foreach (PropertyInfo pi in GetTypeProperties(t)) { + if (pi.CanRead && (pi.CanWrite || anonymous) && + pi.GetIndexParameters().Length == 0) { + if (PropertyData.HasUsableGetter(pi) || + PropertyData.HasUsableSetter(pi)) { + var pn = RemoveIsPrefix(pi.Name); + // Ignore ambiguous properties + if (names.ContainsKey(pn) && names[pn] > 1) { + continue; + } + PropertyData pd = new PropertyMap2.PropertyData() { + Name = pi.Name, + Prop = pi + }; + ret.Add(pd); + } + } + } + ValuePropertyLists.Add(t, ret); + return ret; + } + } + + public static bool ExceedsKnownLength(Stream inStream, long size) { + return (inStream is MemoryStream) && (size > (inStream.Length - + inStream.Position)); + } + + public static void SkipStreamToEnd(Stream inStream) { + if (inStream is MemoryStream) { + inStream.Position = inStream.Length; + } + } + + public static bool FirstElement(int[] index, int[] dimensions) { + foreach (var d in dimensions) { + if (d == 0) { + return false; + } + } + return true; + } + + public static bool NextElement(int[] index, int[] dimensions) { + for (var i = dimensions.Length - 1; i >= 0; --i) { + if (dimensions[i] > 0) { + ++index[i]; + if (index[i] >= dimensions[i]) { + index[i] = 0; + } else { + return true; + } + } + } + return false; + } + + public static CBORObject BuildCBORArray(int[] dimensions) { + int zeroPos = dimensions.Length; + for (var i = 0; i < dimensions.Length; ++i) { + if (dimensions[i] == 0) { + { + zeroPos = i; + } + break; + } + } + int arraydims = zeroPos - 1; + if (arraydims <= 0) { + return CBORObject.NewArray(); + } + var stack = new CBORObject[zeroPos]; + var index = new int[zeroPos]; + var stackpos = 0; + CBORObject ret = CBORObject.NewArray(); + stack[0] = ret; + index[0] = 0; + for (var i = 0; i < dimensions[0]; ++i) { + ret.Add(CBORObject.NewArray()); + } + ++stackpos; + while (stackpos > 0) { + int curindex = index[stackpos - 1]; + if (curindex < stack[stackpos - 1].Count) { + CBORObject subobj = stack[stackpos - 1][curindex]; + if (stackpos < zeroPos) { + stack[stackpos] = subobj; + index[stackpos] = 0; + for (var i = 0; i < dimensions[stackpos]; ++i) { + subobj.Add(CBORObject.NewArray()); + } + ++index[stackpos - 1]; + ++stackpos; + } else { + ++index[stackpos - 1]; + } + } else { + --stackpos; + } + } + return ret; + } + + private static CBORObject GetCBORObject(CBORObject cbor, int[] index) { + CBORObject ret = cbor; + foreach (var i in index) { + ret = ret[i]; + } + return ret; + } + + private static void SetCBORObject( + CBORObject cbor, + int[] index, + CBORObject obj) { + CBORObject ret = cbor; + for (var i = 0; i < index.Length - 1; ++i) { + ret = ret[index[i]]; + } + int ilen = index[index.Length - 1]; + while (ilen >= ret.Count) { + { + ret.Add(CBORObject.Null); + } + } + ret[ilen] = obj; + } + + public static Array FillArray( + Array arr, + Type elementType, + CBORObject cbor, + CBORTypeMapper mapper, + PODOptions options, + int depth) { + int rank = arr.Rank; + if (rank == 0) { + return arr; + } + if (rank == 1) { + int len = arr.GetLength(0); + for (var i = 0; i < len; ++i) { + object item = cbor[i].ToObject( + elementType, + mapper, + options, + depth + 1); + arr.SetValue(item, i); + } + return arr; + } + var index = new int[rank]; + var dimensions = new int[rank]; + for (var i = 0; i < rank; ++i) { + dimensions[i] = arr.GetLength(i); + } + if (!FirstElement(index, dimensions)) { + return arr; + } + do { + object item = GetCBORObject( + cbor, + index).ToObject( + elementType, + mapper, + options, + depth + 1); + arr.SetValue(item, index); + } while (NextElement(index, dimensions)); + return arr; + } + + public static int[] GetDimensions(CBORObject obj) { + if (obj.Type != CBORType.Array) { + throw new CBORException(); + } + // Common cases + if (obj.Count == 0) { + return new int[] { 0 }; + } + if (obj[0].Type != CBORType.Array) { + return new int[] { obj.Count }; + } + // Complex cases + var list = new List(); + list.Add(obj.Count); + while (obj.Type == CBORType.Array && + obj.Count > 0 && obj[0].Type == CBORType.Array) { + list.Add(obj[0].Count); + obj = obj[0]; + } + return list.ToArray(); + } + + public static object ObjectToEnum(CBORObject obj, Type enumType) { + Type utype = Enum.GetUnderlyingType(enumType); + object ret = null; + if (obj.Type == CBORType.Number && obj.IsIntegral) { + ret = Enum.ToObject(enumType, TypeToIntegerObject(obj, utype)); + if (!Enum.IsDefined(enumType, ret)) { + throw new CBORException("Unrecognized enum value: " + + obj.ToString()); + } + return ret; + } else if (obj.Type == CBORType.TextString) { + var nameString = obj.AsString(); + foreach (var name in Enum.GetNames(enumType)) { + if (nameString.Equals(name)) { + return Enum.Parse(enumType, name); + } + } + throw new CBORException("Not found: " + obj.ToString()); + } else { + throw new CBORException("Unrecognized enum value: " + + obj.ToString()); + } + } + + public static object EnumToObject(Enum value) { + return value.ToString(); + } + + public static object EnumToObjectAsInteger(Enum value) { + Type t = Enum.GetUnderlyingType(value.GetType()); + if (t.Equals(typeof(ulong))) { + ulong uvalue = Convert.ToUInt64(value); + return EInteger.FromUInt64(uvalue); + } + return t.Equals(typeof(long)) ? Convert.ToInt64(value) : + (t.Equals(typeof(uint)) ? Convert.ToInt64(value) : + Convert.ToInt32(value)); + } + + public static object FindOneArgumentMethod( + object obj, + string name, + Type argtype) { + return GetTypeMethod(obj.GetType(), name, new[] { argtype }); + } + + public static object InvokeOneArgumentMethod( + object methodInfo, + object obj, + object argument) { + return ((MethodInfo)methodInfo).Invoke(obj, new[] { argument }); + } + + public static byte[] UUIDToBytes(Guid guid) { + var bytes2 = new byte[16]; + var bytes = guid.ToByteArray(); + Array.Copy(bytes, bytes2, 16); + // Swap the bytes to conform with the UUID RFC + bytes2[0] = bytes[3]; + bytes2[1] = bytes[2]; + bytes2[2] = bytes[1]; + bytes2[3] = bytes[0]; + bytes2[4] = bytes[5]; + bytes2[5] = bytes[4]; + bytes2[6] = bytes[7]; + bytes2[7] = bytes[6]; + return bytes2; + } + + private static bool StartsWith(string str, string pfx) { + return str != null && str.Length >= pfx.Length && + str.Substring(0, pfx.Length).Equals(pfx); + } + + private static object TypeToIntegerObject(CBORObject objThis, Type t) { + if (t.Equals(typeof(int))) { + return objThis.AsInt32(); + } + if (t.Equals(typeof(short))) { + return objThis.AsInt16(); + } + if (t.Equals(typeof(ushort))) { + return objThis.AsUInt16(); + } + if (t.Equals(typeof(byte))) { + return objThis.AsByte(); + } + if (t.Equals(typeof(sbyte))) { + return objThis.AsSByte(); + } + if (t.Equals(typeof(long))) { + return objThis.AsInt64(); + } + if (t.Equals(typeof(uint))) { + return objThis.AsUInt32(); + } + if (t.Equals(typeof(ulong))) { + return objThis.AsUInt64(); + } + throw new CBORException("Type not supported"); + } + + public static object TypeToObject( + CBORObject objThis, + Type t, + CBORTypeMapper mapper, + PODOptions options, + int depth) { + if (t.Equals(typeof(int))) { + return objThis.AsInt32(); + } + if (t.Equals(typeof(short))) { + return objThis.AsInt16(); + } + if (t.Equals(typeof(ushort))) { + return objThis.AsUInt16(); + } + if (t.Equals(typeof(byte))) { + return objThis.AsByte(); + } + if (t.Equals(typeof(sbyte))) { + return objThis.AsSByte(); + } + if (t.Equals(typeof(long))) { + return objThis.AsInt64(); + } + if (t.Equals(typeof(uint))) { + return objThis.AsUInt32(); + } + if (t.Equals(typeof(ulong))) { + return objThis.AsUInt64(); + } + if (t.Equals(typeof(double))) { + return objThis.AsDouble(); + } + if (t.Equals(typeof(decimal))) { + return objThis.AsDecimal(); + } + if (t.Equals(typeof(float))) { + return objThis.AsSingle(); + } + if (t.Equals(typeof(bool))) { + return objThis.AsBoolean(); + } + if (t.Equals(typeof(char))) { +if (objThis.Type == CBORType.TextString) { + string s = objThis.AsString(); + if (s.Length != 1) { + throw new CBORException("Can't convert to char"); +} + return s[0]; +} +if (objThis.IsIntegral && objThis.CanTruncatedIntFitInInt32()) { + int c = objThis.AsInt32(); + if (c < 0 || c >= 0x10000) { + throw new CBORException("Can't convert to char"); +} + return (char)c; +} +throw new CBORException("Can't convert to char"); + } + if (t.Equals(typeof(DateTime))) { + return new CBORDateConverter().FromCBORObject(objThis); + } + if (t.Equals(typeof(Guid))) { + return new CBORUuidConverter().FromCBORObject(objThis); + } + if (t.Equals(typeof(Uri))) { + return new CBORUriConverter().FromCBORObject(objThis); + } + if (t.Equals(typeof(EDecimal))) { + return objThis.AsEDecimal(); + } + if (t.Equals(typeof(EFloat))) { + return objThis.AsEFloat(); + } + if (t.Equals(typeof(EInteger))) { + return objThis.AsEInteger(); + } + if (t.Equals(typeof(ERational))) { + return objThis.AsERational(); + } + if (IsAssignableFrom(typeof(Enum), t)) { + return ObjectToEnum(objThis, t); + } + if (objThis.Type == CBORType.ByteString) { + if (t.Equals(typeof(byte[]))) { + byte[] bytes = objThis.GetByteString(); + var byteret = new byte[bytes.Length]; + Array.Copy(bytes, 0, byteret, 0, byteret.Length); + return byteret; + } + } + if (objThis.Type == CBORType.Array) { + Type objectType = typeof(object); + var isList = false; + object listObject = null; +#if NET40 || NET20 + if (IsAssignableFrom(typeof(Array), t)) { +Type elementType = t.GetElementType(); + Array array = Array.CreateInstance( + elementType, + GetDimensions(objThis)); + return FillArray(array, elementType, objThis, mapper, options, depth); + } + if (t.IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isList = td.Equals(typeof(List<>)) || td.Equals(typeof(IList<>)) || + td.Equals(typeof(ICollection<>)) || + td.Equals(typeof(IEnumerable<>)); + } + isList = isList && t.GetGenericArguments().Length == 1; + if (isList) { + objectType = t.GetGenericArguments()[0]; + Type listType = typeof(List<>).MakeGenericType(objectType); + listObject = Activator.CreateInstance(listType); + } +#else + if (IsAssignableFrom(typeof(Array), t)) { + Type elementType = t.GetElementType(); + Array array = Array.CreateInstance( + elementType, + GetDimensions(objThis)); + return FillArray(array, elementType, objThis, mapper, options, depth); + } + if (t.GetTypeInfo().IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isList = (td.Equals(typeof(List<>)) || + td.Equals(typeof(IList<>)) || + td.Equals(typeof(ICollection<>)) || + td.Equals(typeof(IEnumerable<>))); + } + isList = (isList && t.GenericTypeArguments.Length == 1); + if (isList) { + objectType = t.GenericTypeArguments[0]; + Type listType = typeof(List<>).MakeGenericType(objectType); + listObject = Activator.CreateInstance(listType); + } +#endif + if (listObject == null) { + if (t.Equals(typeof(IList)) || + t.Equals(typeof(ICollection)) || t.Equals(typeof(IEnumerable))) { + listObject = new List(); + objectType = typeof(object); + } + } + if (listObject != null) { + System.Collections.IList ie = (System.Collections.IList)listObject; + foreach (CBORObject value in objThis.Values) { + ie.Add(value.ToObject(objectType, mapper, options, depth + 1)); + } + return listObject; + } + } + if (objThis.Type == CBORType.Map) { + var isDict = false; + Type keyType = null; + Type valueType = null; + object dictObject = null; +#if NET40 || NET20 + isDict = t.IsGenericType; + if (t.IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isDict = td.Equals(typeof(Dictionary<,>)) || + td.Equals(typeof(IDictionary<,>)); + } + // DebugUtility.Log("list=" + isDict); + isDict = isDict && t.GetGenericArguments().Length == 2; + // DebugUtility.Log("list=" + isDict); + if (isDict) { + keyType = t.GetGenericArguments()[0]; + valueType = t.GetGenericArguments()[1]; + Type listType = typeof(Dictionary<,>).MakeGenericType( + keyType, + valueType); + dictObject = Activator.CreateInstance(listType); + } +#else + isDict = (t.GetTypeInfo().IsGenericType); + if (t.GetTypeInfo().IsGenericType) { + Type td = t.GetGenericTypeDefinition(); + isDict = (td.Equals(typeof(Dictionary<,>)) || + td.Equals(typeof(IDictionary<,>))); + } + //DebugUtility.Log("list=" + isDict); + isDict = (isDict && t.GenericTypeArguments.Length == 2); + //DebugUtility.Log("list=" + isDict); + if (isDict) { + keyType = t.GenericTypeArguments[0]; + valueType = t.GenericTypeArguments[1]; + Type listType = typeof(Dictionary<,>).MakeGenericType( + keyType, + valueType); + dictObject = Activator.CreateInstance(listType); + } +#endif + if (dictObject == null) { + if (t.Equals(typeof(IDictionary))) { + dictObject = new Dictionary(); + keyType = typeof(object); + valueType = typeof(object); + } + } + if (dictObject != null) { + System.Collections.IDictionary idic = + (System.Collections.IDictionary)dictObject; + foreach (CBORObject key in objThis.Keys) { + CBORObject value = objThis[key]; + idic.Add( + key.ToObject(keyType, mapper, options, depth + 1), + value.ToObject(valueType, mapper, options, depth + 1)); + } + return dictObject; + } + if (mapper != null) { + if (!mapper.FilterTypeName(t.FullName)) { + throw new CBORException("Type " + t.FullName + + " not supported"); + } + } else { + if (t.FullName != null && (StartsWith(t.FullName, "Microsoft.Win32." +) || + StartsWith(t.FullName, "System.IO."))) { + throw new CBORException("Type " + t.FullName + + " not supported"); + } + if (StartsWith(t.FullName, "System.") && + !HasCustomAttribute(t, "System.SerializableAttribute")) { + throw new CBORException("Type " + t.FullName + + " not supported"); + } + } + var values = new List>(); + foreach (string key in PropertyMap2.GetPropertyNames( + t, + options != null ? options.UseCamelCase : true)) { + if (objThis.ContainsKey(key)) { + CBORObject cborValue = objThis[key]; + var dict = new KeyValuePair( + key, + cborValue); + values.Add(dict); + } + } + return PropertyMap2.ObjectWithProperties( + t, + values, + mapper, + options, + depth); + } else { + throw new CBORException(); + } + } + + public static object ObjectWithProperties( + Type t, + IEnumerable> keysValues, + CBORTypeMapper mapper, + PODOptions options, + int depth) { + object o = Activator.CreateInstance(t); + var dict = new Dictionary(); + foreach (var kv in keysValues) { + var name = kv.Key; + dict[name] = kv.Value; + } + foreach (PropertyData key in GetPropertyList(o.GetType())) { + if (!key.HasUsableSetter() || !key.HasUsableGetter()) { + // Require properties to have both a setter and + // a getter to be eligible for setting + continue; + } + var name = key.GetAdjustedName(options != null ? options.UseCamelCase : + true); + if (dict.ContainsKey(name)) { + object dobj = dict[name].ToObject( + key.Prop.PropertyType, + mapper, + options, + depth + 1); + key.Prop.SetValue(o, dobj, null); + } + } + return o; + } + + public static IEnumerable> + GetProperties(Object o) { + return GetProperties(o, true); + } + + public static IEnumerable + GetPropertyNames(Type t, bool useCamelCase) { + foreach (PropertyData key in GetPropertyList(t)) { + yield return key.GetAdjustedName(useCamelCase); + } + } + + public static IEnumerable> + GetProperties(Object o, bool useCamelCase) { + foreach (PropertyData key in GetPropertyList(o.GetType())) { + if (!key.HasUsableGetter()) { + continue; + } + yield return new KeyValuePair( + key.GetAdjustedName(useCamelCase), + key.Prop.GetValue(o, null)); + } + } + + public static void BreakDownDateTime( + DateTime bi, + EInteger[] year, + int[] lf) { +#if NET20 + DateTime dt = bi.ToUniversalTime(); +#else + DateTime dt = TimeZoneInfo.ConvertTime(bi, TimeZoneInfo.Utc); +#endif + year[0] = EInteger.FromInt32(dt.Year); + lf[0] = dt.Month; + lf[1] = dt.Day; + lf[2] = dt.Hour; + lf[3] = dt.Minute; + lf[4] = dt.Second; + // lf[5] is the number of nanoseconds + lf[5] = (int)(dt.Ticks % 10000000L) * 100; + } + + public static DateTime BuildUpDateTime(EInteger year, int[] dt) { + return new DateTime( + year.ToInt32Checked(), + dt[0], + dt[1], + dt[2], + dt[3], + dt[4], + DateTimeKind.Utc).AddMinutes(-dt[6]).AddTicks((long)(dt[5] / 100)); + } + } +} diff --git a/PeterO/Cbor/SharedRefs.cs b/PeterO/Cbor/SharedRefs.cs new file mode 100644 index 00000000..d714d731 --- /dev/null +++ b/PeterO/Cbor/SharedRefs.cs @@ -0,0 +1,55 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Collections.Generic; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal class SharedRefs { + private readonly IList sharedObjects; + + public SharedRefs() { + this.sharedObjects = new List(); + } + + public void AddObject(CBORObject obj) { + this.sharedObjects.Add(obj); + } + + public CBORObject GetObject(long smallIndex) { + if (smallIndex < 0) { + throw new CBORException("Unexpected index"); + } + if (smallIndex > Int32.MaxValue) { + throw new CBORException("Index " + smallIndex + + " is bigger than supported "); + } + var index = (int)smallIndex; + if (index >= this.sharedObjects.Count) { + throw new CBORException("Index " + index + " is not valid"); + } + return this.sharedObjects[index]; + } + + public CBORObject GetObject(EInteger bigIndex) { + if (bigIndex.Sign < 0) { + throw new CBORException("Unexpected index"); + } + if (!bigIndex.CanFitInInt32()) { + throw new CBORException("Index " + bigIndex + + " is bigger than supported "); + } + var index = (int)bigIndex; + if (index >= this.sharedObjects.Count) { + throw new CBORException("Index " + index + " is not valid"); + } + return this.sharedObjects[index]; + } + } +} diff --git a/PeterO/Cbor/StringOutput.cs b/PeterO/Cbor/StringOutput.cs new file mode 100644 index 00000000..f1b0b744 --- /dev/null +++ b/PeterO/Cbor/StringOutput.cs @@ -0,0 +1,114 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.IO; +using System.Text; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + internal sealed class StringOutput { + private readonly StringBuilder builder; + private readonly Stream outputStream; + + public StringOutput(StringBuilder builder) { + this.builder = builder; + this.outputStream = null; + } + + public StringOutput(Stream outputStream) { + this.outputStream = outputStream; + this.builder = null; + } + + public void WriteString(string str) { + if (this.outputStream != null) { + if (str.Length == 1) { + this.WriteCodePoint((int)str[0]); + } else { + if (DataUtilities.WriteUtf8( + str, + 0, + str.Length, + this.outputStream, + false) < 0) { + throw new ArgumentException("str has an unpaired surrogate"); + } + } + } else { + this.builder.Append(str); + } + } + + public void WriteString(string str, int index, int length) { + if (this.outputStream != null) { + if (length == 1) { + this.WriteCodePoint((int)str[index]); + } else { + if ( + DataUtilities.WriteUtf8( + str, + index, + length, + this.outputStream, + false) < 0) { + throw new ArgumentException("str has an unpaired surrogate"); + } + } + } else { + this.builder.Append(str, index, length); + } + } + + public void WriteCodePoint(int codePoint) { + if (codePoint < 0) { + throw new ArgumentException("codePoint (" + codePoint + + ") is less than 0"); + } + if (codePoint > 0x10ffff) { + throw new ArgumentException("codePoint (" + codePoint + + ") is more than " + 0x10ffff); + } + if (this.outputStream != null) { + if (codePoint < 0x80) { + this.outputStream.WriteByte((byte)codePoint); + } else if (codePoint <= 0x7ff) { + this.outputStream.WriteByte((byte)(0xc0 | ((codePoint >> 6) & 0x1f))); + this.outputStream.WriteByte((byte)(0x80 | (codePoint & 0x3f))); + } else if (codePoint <= 0xffff) { + if ((codePoint & 0xf800) == 0xd800) { + throw new ArgumentException("ch is a surrogate"); + } + this.outputStream.WriteByte((byte)(0xe0 | ((codePoint >> 12) & + 0x0f))); + this.outputStream.WriteByte((byte)(0x80 | ((codePoint >> 6) & 0x3f))); + this.outputStream.WriteByte((byte)(0x80 | (codePoint & 0x3f))); + } else { + this.outputStream.WriteByte((byte)(0xf0 | ((codePoint >> 18) & + 0x08))); + this.outputStream.WriteByte((byte)(0x80 | ((codePoint >> 12) & + 0x3f))); + this.outputStream.WriteByte((byte)(0x80 | ((codePoint >> 6) & 0x3f))); + this.outputStream.WriteByte((byte)(0x80 | (codePoint & 0x3f))); + } + } else { + if ((codePoint & 0xfff800) == 0xd800) { + throw new ArgumentException("ch is a surrogate"); + } + if (codePoint <= 0xffff) { + { this.builder.Append((char)codePoint); + } + } else if (codePoint <= 0x10ffff) { + this.builder.Append((char)((((codePoint - 0x10000) >> 10) & + 0x3ff) + 0xd800)); + this.builder.Append((char)(((codePoint - 0x10000) & 0x3ff) + 0xdc00)); + } + } + } + } +} diff --git a/PeterO/Cbor/StringRefs.cs b/PeterO/Cbor/StringRefs.cs new file mode 100644 index 00000000..d1f648af --- /dev/null +++ b/PeterO/Cbor/StringRefs.cs @@ -0,0 +1,113 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Collections.Generic; +using PeterO; +using PeterO.Numbers; + +namespace PeterO.Cbor { + /// + internal class StringRefs { + private readonly List> stack; + + public StringRefs() { + this.stack = new List>(); + var firstItem = new List(); + this.stack.Add(firstItem); + } + + public void Push() { + var firstItem = new List(); + this.stack.Add(firstItem); + } + + public void Pop() { + #if DEBUG + if (this.stack.Count <= 0) { + throw new ArgumentException("this.stack.Count (" + this.stack.Count + + ") is not greater than " + "0 "); + } + #endif + this.stack.RemoveAt(this.stack.Count - 1); + } + + public void AddStringIfNeeded(CBORObject str, int lengthHint) { + #if DEBUG + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + if (!(str.Type == CBORType.ByteString || str.Type == + CBORType.TextString)) { + throw new + ArgumentException( + "doesn't satisfy str.Type== ByteString or TextString"); + } + if (lengthHint < 0) { + throw new ArgumentException("lengthHint (" + lengthHint + + ") is less than " + "0 "); + } + #endif + var addStr = false; + List lastList = this.stack[this.stack.Count - 1]; + if (lastList.Count < 24) { + addStr |= lengthHint >= 3; + } else if (lastList.Count < 256) { + addStr |= lengthHint >= 4; + } else if (lastList.Count < 65536) { + addStr |= lengthHint >= 5; + } else { + // NOTE: lastList's size can't be higher than (2^64)-1 + addStr |= lengthHint >= 7; + } + // NOTE: An additional branch, with lengthHint >= 11, would + // be needed if the size could be higher than (2^64)-1 + if (addStr) { + lastList.Add(str); + } + } + + public CBORObject GetString(long smallIndex) { + if (smallIndex < 0) { + throw new CBORException("Unexpected index"); + } + if (smallIndex > Int32.MaxValue) { + throw new CBORException("Index " + smallIndex + + " is bigger than supported "); + } + var index = (int)smallIndex; + List lastList = this.stack[this.stack.Count - 1]; + if (index >= lastList.Count) { + throw new CBORException("Index " + index + " is not valid"); + } + CBORObject ret = lastList[index]; + // Byte strings are mutable, so make a copy + return (ret.Type == CBORType.ByteString) ? + CBORObject.FromObject(ret.GetByteString()) : ret; + } + + public CBORObject GetString(EInteger bigIndex) { + if (bigIndex.Sign < 0) { + throw new CBORException("Unexpected index"); + } + if (!bigIndex.CanFitInInt32()) { + throw new CBORException("Index " + bigIndex + + " is bigger than supported "); + } + var index = (int)bigIndex; + List lastList = this.stack[this.stack.Count - 1]; + if (index >= lastList.Count) { + throw new CBORException("Index " + index + " is not valid"); + } + CBORObject ret = lastList[index]; + // Byte strings are mutable, so make a copy + return (ret.Type == CBORType.ByteString) ? + CBORObject.FromObject(ret.GetByteString()) : ret; + } + } +} diff --git a/PeterO/Cbor/URIUtility.cs b/PeterO/Cbor/URIUtility.cs new file mode 100644 index 00000000..05a8afd3 --- /dev/null +++ b/PeterO/Cbor/URIUtility.cs @@ -0,0 +1,1129 @@ +/* +Written in 2013 by Peter Occil. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ + +using System; +using System.Text; + +namespace PeterO.Cbor { + /// + internal static class URIUtility { + /// + internal enum ParseMode { + /// + IRIStrict, + + /// + URIStrict, + + /// + IRILenient, + + /// + URILenient, + + /// + IRISurrogateLenient + } + + private const string HexChars = "0123456789ABCDEF"; + + private static void appendAuthority( + StringBuilder builder, + string refValue, + int[] segments) { + if (segments[2] >= 0) { + builder.Append("//"); + builder.Append( + refValue.Substring( + segments[2], + segments[3] - segments[2])); + } + } + + private static void appendFragment( + StringBuilder builder, + string refValue, + int[] segments) { + if (segments[8] >= 0) { + builder.Append('#'); + builder.Append( + refValue.Substring( + segments[8], + segments[9] - segments[8])); + } + } + + private static void appendNormalizedPath( + StringBuilder builder, + string refValue, + int[] segments) { + builder.Append( + normalizePath( + refValue.Substring( + segments[4], + segments[5] - segments[4]))); + } + + private static void appendPath( + StringBuilder builder, + string refValue, + int[] segments) { + builder.Append( + refValue.Substring( + segments[4], + segments[5] - segments[4])); + } + + private static void appendQuery( + StringBuilder builder, + string refValue, + int[] segments) { + if (segments[6] >= 0) { + builder.Append('?'); + builder.Append( + refValue.Substring( + segments[6], + segments[7] - segments[6])); + } + } + + private static void appendScheme( + StringBuilder builder, + string refValue, + int[] segments) { + if (segments[0] >= 0) { + builder.Append( + refValue.Substring( + segments[0], + segments[1] - segments[0])); + builder.Append(':'); + } + } + + /// + public static string escapeURI(string s, int mode) { + if (s == null) { + return null; + } + int[] components = null; + if (mode == 1) { + components = ( + s == null) ? null : splitIRI( + s, + 0, + s.Length, + ParseMode.IRIStrict); + if (components == null) { + return null; + } + } else { + components = (s == null) ? null : splitIRI( + s, + 0, + s.Length, + ParseMode.IRISurrogateLenient); + } + var index = 0; + int valueSLength = s.Length; + var builder = new StringBuilder(); + while (index < valueSLength) { + int c = s[index]; + if ((c & 0xfc00) == 0xd800 && index + 1 < valueSLength && + (s[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (s[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + c = 0xfffd; + } + if (mode == 0 || mode == 3) { + if (c == '%' && mode == 3) { + // Check for illegal percent encoding + if (index + 2 >= valueSLength || !isHexChar(s[index + 1]) || + !isHexChar(s[index + 2])) { + percentEncodeUtf8(builder, c); + } else { + if (c <= 0xffff) { + builder.Append((char)c); + } else if (c <= 0x10ffff) { + builder.Append((char)((((c - 0x10000) >> 10) & 0x3ff) + + 0xd800)); + builder.Append((char)(((c - 0x10000) & 0x3ff) + 0xdc00)); + } + } + ++index; + continue; + } + if (c >= 0x7f || c <= 0x20 || + ((c & 0x7f) == c && "{}|^\\`<>\"".IndexOf((char)c) >= 0)) { + percentEncodeUtf8(builder, c); + } else if (c == '[' || c == ']') { + if (components != null && index >= components[2] && index < + components[3]) { + // within the authority component, so don't percent-encode + if (c <= 0xffff) { + builder.Append((char)c); + } else if (c <= 0x10ffff) { + builder.Append((char)((((c - 0x10000) >> 10) & 0x3ff) + + 0xd800)); + builder.Append((char)(((c - 0x10000) & 0x3ff) + 0xdc00)); + } + } else { + // percent encode + percentEncodeUtf8(builder, c); + } + } else { + if (c <= 0xffff) { + builder.Append((char)c); + } else if (c <= 0x10ffff) { + builder.Append((char)((((c - 0x10000) >> 10) & 0x3ff) + 0xd800)); + builder.Append((char)(((c - 0x10000) & 0x3ff) + 0xdc00)); + } + } + } else if (mode == 1 || mode == 2) { + if (c >= 0x80) { + percentEncodeUtf8(builder, c); + } else if (c == '[' || c == ']') { + if (components != null && index >= components[2] && index < + components[3]) { + // within the authority component, so don't percent-encode + if (c <= 0xffff) { + builder.Append((char)c); + } else if (c <= 0x10ffff) { + builder.Append((char)((((c - 0x10000) >> 10) & 0x3ff) + + 0xd800)); + builder.Append((char)(((c - 0x10000) & 0x3ff) + 0xdc00)); + } + } else { + // percent encode + percentEncodeUtf8(builder, c); + } + } else { + if (c <= 0xffff) { + builder.Append((char)c); + } else if (c <= 0x10ffff) { + builder.Append((char)((((c - 0x10000) >> 10) & 0x3ff) + 0xd800)); + builder.Append((char)(((c - 0x10000) & 0x3ff) + 0xdc00)); + } + } + } + ++index; + } + return builder.ToString(); + } + + /// + public static bool hasScheme(string refValue) { + int[] segments = (refValue == null) ? null : splitIRI( + refValue, + 0, + refValue.Length, + ParseMode.IRIStrict); + return segments != null && segments[0] >= 0; + } + + /// + public static bool hasSchemeForURI(string refValue) { + int[] segments = (refValue == null) ? null : splitIRI( + refValue, + 0, + refValue.Length, + ParseMode.URIStrict); + return segments != null && segments[0] >= 0; + } + + private static bool isHexChar(char c) { + return (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'); + } + + private static bool isIfragmentChar(int c) { + // '%' omitted + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ((c & 0x7F) == c && "/?-._~:@!$&'()*+,;=".IndexOf((char)c) >= 0) || + (c >= 0xa0 && c <= 0xd7ff) || (c >= 0xf900 && c <= 0xfdcf) || + (c >= 0xfdf0 && c <= 0xffef) || + (c >= 0x10000 && c <= 0xefffd && (c & 0xfffe) != 0xfffe); + } + + private static bool isIpchar(int c) { + // '%' omitted + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ((c & 0x7F) == c && "/-._~:@!$&'()*+,;=".IndexOf((char)c) >= 0) || + (c >= 0xa0 && c <= 0xd7ff) || (c >= 0xf900 && c <= 0xfdcf) || + (c >= 0xfdf0 && c <= 0xffef) || + (c >= 0x10000 && c <= 0xefffd && (c & 0xfffe) != 0xfffe); + } + + private static bool isIqueryChar(int c) { + // '%' omitted + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ((c & 0x7F) == c && "/?-._~:@!$&'()*+,;=".IndexOf((char)c) >= 0) || + (c >= 0xa0 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfdcf) || + (c >= 0xfdf0 && c <= 0xffef) || + (c >= 0x10000 && c <= 0x10fffd && (c & 0xfffe) != 0xfffe); + } + + private static bool isIRegNameChar(int c) { + // '%' omitted + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ((c & 0x7F) == c && "-._~!$&'()*+,;=".IndexOf((char)c) >= 0) || + (c >= 0xa0 && c <= 0xd7ff) || (c >= 0xf900 && c <= 0xfdcf) || + (c >= 0xfdf0 && c <= 0xffef) || + (c >= 0x10000 && c <= 0xefffd && (c & 0xfffe) != 0xfffe); + } + + private static bool isIUserInfoChar(int c) { + // '%' omitted + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ((c & 0x7F) == c && "-._~:!$&'()*+,;=".IndexOf((char)c) >= 0) || + (c >= 0xa0 && c <= 0xd7ff) || (c >= 0xf900 && c <= 0xfdcf) || + (c >= 0xfdf0 && c <= 0xffef) || + (c >= 0x10000 && c <= 0xefffd && (c & 0xfffe) != 0xfffe); + } + + /// + public static bool isValidCurieReference(string s, int offset, int length) { + if (s == null) { + return false; + } + if (offset < 0) { + throw new ArgumentException("offset (" + offset + ") is less than " + + "0 "); + } + if (offset > s.Length) { + throw new ArgumentException("offset (" + offset + ") is more than " + + s.Length); + } + if (length < 0) { + throw new ArgumentException( + "length (" + length + ") is less than " + "0 "); + } + if (length > s.Length) { + throw new ArgumentException( + "length (" + length + ") is more than " + s.Length); + } + if (s.Length - offset < length) { + throw new ArgumentException( + "s's length minus " + offset + " (" + (s.Length - offset) + + ") is less than " + length); + } + if (length == 0) { + return true; + } + int index = offset; + int valueSLength = offset + length; + var state = 0; + if (index + 2 <= valueSLength && s[index] == '/' && s[index + 1] == '/') { + // has an authority, which is not allowed + return false; + } + state = 0; // IRI Path + while (index < valueSLength) { + // Get the next Unicode character + int c = s[index]; + if ((c & 0xfc00) == 0xd800 && index + 1 < valueSLength && + (s[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (s[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + // error + return false; + } + if (c == '%') { + // Percent encoded character + if (index + 2 < valueSLength && isHexChar(s[index + 1]) && + isHexChar(s[index + 2])) { + index += 3; + continue; + } + return false; + } + if (state == 0) { // Path + if (c == '?') { + state = 1; // move to query state + } else if (c == '#') { + state = 2; // move to fragment state + } else if (!isIpchar(c)) { + return false; + } + ++index; + } else if (state == 1) { // Query + if (c == '#') { + state = 2; // move to fragment state + } else if (!isIqueryChar(c)) { + return false; + } + ++index; + } else if (state == 2) { // Fragment + if (!isIfragmentChar(c)) { + return false; + } + ++index; + } + } + return true; + } + + public static bool isValidIRI(string s) { + return ((s == null) ? + null : splitIRI( + s, + 0, + s.Length, + ParseMode.IRIStrict)) != null; + } + + private const string ValueDotSlash = "." + "/"; + private const string ValueSlashDot = "/" + "."; + + private static string normalizePath(string path) { + int len = path.Length; + if (len == 0 || path.Equals("..") || path.Equals(".")) { + return String.Empty; + } + if (path.IndexOf(ValueSlashDot, StringComparison.Ordinal) < 0 && + path.IndexOf( + ValueDotSlash, + StringComparison.Ordinal) < 0) { + return path; + } + var builder = new StringBuilder(); + var index = 0; + while (index < len) { + char c = path[index]; + if ((index + 3 <= len && c == '/' && path[index + 1] == '.' && + path[index + 2] == '/') || (index + 2 == len && c == '.' && + path[index + 1] == '.')) { + // begins with "/./" or is ".."; + // move index by 2 + index += 2; + continue; + } + if (index + 3 <= len && c == '.' && + path[index + 1] == '.' && path[index + 2] == '/') { + // begins with "../"; + // move index by 3 + index += 3; + continue; + } + if ((index + 2 <= len && c == '.' && + path[index + 1] == '/') || (index + 1 == len && c == '.')) { + // begins with "./" or is "."; + // move index by 1 + ++index; + continue; + } + if (index + 2 == len && c == '/' && + path[index + 1] == '.') { + // is "/."; append '/' and break + builder.Append('/'); + break; + } + if (index + 3 == len && c == '/' && + path[index + 1] == '.' && path[index + 2] == '.') { + // is "/.."; remove last segment, + // append "/" and return + int index2 = builder.Length - 1; + string builderString = builder.ToString(); + while (index2 >= 0) { + if (builderString[index2] == '/') { + break; + } + --index2; + } + if (index2 < 0) { + index2 = 0; + } + builder.Length = index2; + builder.Append('/'); + break; + } + if (index + 4 <= len && c == '/' && path[index + 1] == '.' && + path[index + 2] == '.' && path[index + 3] == '/') { + // begins with "/../"; remove last segment + int index2 = builder.Length - 1; + string builderString = builder.ToString(); + while (index2 >= 0) { + if (builderString[index2] == '/') { + break; + } + --index2; + } + if (index2 < 0) { + index2 = 0; + } + builder.Length = index2; + index += 3; + continue; + } + builder.Append(c); + ++index; + while (index < len) { + // Move the rest of the + // path segment until the next '/' + c = path[index]; + if (c == '/') { + break; + } + builder.Append(c); + ++index; + } + } + return builder.ToString(); + } + + private static int parseDecOctet( + string s, + int index, + int endOffset, + int c, + int delim) { + if (c >= '1' && c <= '9' && index + 2 < endOffset && + s[index + 1] >= '0' && s[index + 1] <= '9' && + s[index + 2] == delim) { + return ((c - '0') * 10) + (s[index + 1] - '0'); + } + if (c == '2' && index + 3 < endOffset && + (s[index + 1] == '5') && (s[index + 2] >= '0' && s[index + 2] <= '5') && + s[index + 3] == delim) { + return 250 + (s[index + 2] - '0'); + } + if (c == '2' && index + 3 < endOffset && + s[index + 1] >= '0' && s[index + 1] <= '4' && + s[index + 2] >= '0' && s[index + 2] <= '9' && + s[index + 3] == delim) { + return 200 + ((s[index + 1] - '0') * 10) + (s[index + 2] - '0'); + } + if (c == '1' && index + 3 < endOffset && + s[index + 1] >= '0' && s[index + 1] <= '9' && + s[index + 2] >= '0' && s[index + 2] <= '9' && + s[index + 3] == delim) { + return 100 + ((s[index + 1] - '0') * 10) + (s[index + 2] - '0'); + } + return (c >= '0' && c <= '9' && index + 1 < endOffset && + s[index + 1] == delim) ? (c - '0') : (-1); + } + + private static int parseIPLiteral(string s, int offset, int endOffset) { + int index = offset; + if (offset == endOffset) { + return -1; + } + // Assumes that the character before offset + // is a '[' + if (s[index] == 'v') { + // IPvFuture + ++index; + var hex = false; + while (index < endOffset) { + char c = s[index]; + if (isHexChar(c)) { + hex = true; + } else { + break; + } + ++index; + } + if (!hex) { + return -1; + } + if (index >= endOffset || s[index] != '.') { + return -1; + } + ++index; + hex = false; + while (index < endOffset) { + char c = s[index]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + ((c & 0x7F) == c && ":-._~!$&'()*+,;=".IndexOf(c) >= 0)) { + hex = true; + } else { + break; + } + ++index; + } + if (!hex) { + return -1; + } + if (index >= endOffset || s[index] != ']') { + return -1; + } + ++index; + return index; + } + if (s[index] == ':' || + isHexChar(s[index])) { + // IPv6 Address + var phase1 = 0; + var phase2 = 0; + var phased = false; + var expectHex = false; + var expectColon = false; + while (index < endOffset) { + char c = s[index]; + if (c == ':' && !expectHex) { + if ((phase1 + (phased ? 1 : 0) + phase2) >= 8) { + return -1; + } + ++index; + if (index < endOffset && s[index] == ':') { + if (phased) { + return -1; + } + phased = true; + ++index; + } + expectHex = true; + expectColon = false; + continue; + } + if ((c >= '0' && c <= '9') && !expectColon && + (phased || (phase1 + (phased ? 1 : 0) + phase2) == 6)) { + // Check for IPv4 address + int decOctet = parseDecOctet(s, index, endOffset, c, '.'); + if (decOctet >= 0) { + if ((phase1 + (phased ? 1 : 0) + phase2) > 6) { + // IPv4 address illegal at this point + return -1; + } else { + // Parse the rest of the IPv4 address + phase2 += 2; + if (decOctet >= 100) { + index += 4; + } else if (decOctet >= 10) { + index += 3; + } else { + index += 2; + } + char tmpc = (index < endOffset) ? s[index] : '\0'; + decOctet = parseDecOctet( + s, + index, + endOffset, + tmpc, + '.'); + if (decOctet >= 100) { + index += 4; + } else if (decOctet >= 10) { + index += 3; + } else if (decOctet >= 0) { + index += 2; + } else { + return -1; + } + tmpc = (index < endOffset) ? s[index] : '\0'; + decOctet = parseDecOctet(s, index, endOffset, tmpc, '.'); + if (decOctet >= 100) { + index += 4; + } else if (decOctet >= 10) { + index += 3; + } else if (decOctet >= 0) { + index += 2; + } else { + return -1; + } + tmpc = (index < endOffset) ? s[index] : '\0'; + decOctet = parseDecOctet(s, index, endOffset, tmpc, ']'); + if (decOctet < 0) { + tmpc = (index < endOffset) ? s[index] : '\0'; + decOctet = parseDecOctet(s, index, endOffset, tmpc, '%'); + } + if (decOctet >= 100) { + index += 3; + } else if (decOctet >= 10) { + index += 2; + } else if (decOctet >= 0) { + ++index; + } else { + return -1; + } + break; + } + } + } + if (isHexChar(c) && !expectColon) { + if (phased) { + ++phase2; + } else { + ++phase1; + } + ++index; + for (var i = 0; i < 3; ++i) { + if (index < endOffset && isHexChar(s[index])) { + ++index; + } else { + break; + } + } + expectHex = false; + expectColon = true; + } else { + break; + } + } + if ((phase1 + phase2) != 8 && !phased) { + return -1; + } + if (phase1 + 1 + phase2 > 8 && phased) { + return -1; + } + if (index >= endOffset) { + return -1; + } + if (s[index] != ']' && s[index] != '%') { + return -1; + } + if (s[index] == '%') { + if (index + 2 < endOffset && s[index + 1] == '2' && + s[index + 2] == '5') { + // Zone identifier in an IPv6 address + // (see RFC6874) + index += 3; + var haveChar = false; + while (index < endOffset) { + char c = s[index]; + if (c == ']') { + return haveChar ? index + 1 : -1; + } + if (c == '%') { + if (index + 2 < endOffset && isHexChar(s[index + 1]) && + isHexChar(s[index + 2])) { + index += 3; + haveChar = true; + continue; + } + return -1; + } + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-' || + c == '~') { + // unreserved character under RFC3986 + ++index; + haveChar = true; + continue; + } + return -1; + } + return -1; + } + return -1; + } + ++index; + return index; + } + return -1; + } + + private static string pathParent( + string refValue, + int startIndex, + int endIndex) { + if (startIndex > endIndex) { + return String.Empty; + } + --endIndex; + while (endIndex >= startIndex) { + if (refValue[endIndex] == '/') { + return refValue.Substring(startIndex, (endIndex + 1) - startIndex); + } + --endIndex; + } + return String.Empty; + } + + private static void percentEncode(StringBuilder buffer, int b) { + buffer.Append('%'); + buffer.Append(HexChars[(b >> 4) & 0x0f]); + buffer.Append(HexChars[b & 0x0f]); + } + + private static void percentEncodeUtf8(StringBuilder buffer, int cp) { + if (cp <= 0x7f) { + buffer.Append('%'); + buffer.Append(HexChars[(cp >> 4) & 0x0f]); + buffer.Append(HexChars[cp & 0x0f]); + } else if (cp <= 0x7ff) { + percentEncode(buffer, 0xc0 | ((cp >> 6) & 0x1f)); + percentEncode(buffer, 0x80 | (cp & 0x3f)); + } else if (cp <= 0xffff) { + percentEncode(buffer, 0xe0 | ((cp >> 12) & 0x0f)); + percentEncode(buffer, 0x80 | ((cp >> 6) & 0x3f)); + percentEncode(buffer, 0x80 | (cp & 0x3f)); + } else { + percentEncode(buffer, 0xf0 | ((cp >> 18) & 0x07)); + percentEncode(buffer, 0x80 | ((cp >> 12) & 0x3f)); + percentEncode(buffer, 0x80 | ((cp >> 6) & 0x3f)); + percentEncode(buffer, 0x80 | (cp & 0x3f)); + } + } + + /// + public static string relativeResolve(string refValue, string baseURI) { + return relativeResolve(refValue, baseURI, ParseMode.IRIStrict); + } + + /// + public static string relativeResolve( + string refValue, + string baseURI, + ParseMode parseMode) { + int[] segments = (refValue == null) ? null : splitIRI( + refValue, + 0, + refValue.Length, + parseMode); + if (segments == null) { + return null; + } + int[] segmentsBase = ( + baseURI == null) ? null : splitIRI( + baseURI, + 0, + baseURI.Length, + parseMode); + if (segmentsBase == null) { + return refValue; + } + var builder = new StringBuilder(); + if (segments[0] >= 0) { // scheme present + appendScheme(builder, refValue, segments); + appendAuthority(builder, refValue, segments); + appendNormalizedPath(builder, refValue, segments); + appendQuery(builder, refValue, segments); + appendFragment(builder, refValue, segments); + } else if (segments[2] >= 0) { // authority present + appendScheme(builder, baseURI, segmentsBase); + appendAuthority(builder, refValue, segments); + appendNormalizedPath(builder, refValue, segments); + appendQuery(builder, refValue, segments); + appendFragment(builder, refValue, segments); + } else if (segments[4] == segments[5]) { + appendScheme(builder, baseURI, segmentsBase); + appendAuthority(builder, baseURI, segmentsBase); + appendPath(builder, baseURI, segmentsBase); + if (segments[6] >= 0) { + appendQuery(builder, refValue, segments); + } else { + appendQuery(builder, baseURI, segmentsBase); + } + appendFragment(builder, refValue, segments); + } else { + appendScheme(builder, baseURI, segmentsBase); + appendAuthority(builder, baseURI, segmentsBase); + if (segments[4] < segments[5] && refValue[segments[4]] == '/') { + appendNormalizedPath(builder, refValue, segments); + } else { + var merged = new StringBuilder(); + if (segmentsBase[2] >= 0 && segmentsBase[4] == segmentsBase[5]) { + merged.Append('/'); + appendPath(merged, refValue, segments); + builder.Append(normalizePath(merged.ToString())); + } else { + merged.Append( + pathParent( + baseURI, + segmentsBase[4], + segmentsBase[5])); + appendPath(merged, refValue, segments); + builder.Append(normalizePath(merged.ToString())); + } + } + appendQuery(builder, refValue, segments); + appendFragment(builder, refValue, segments); + } + return builder.ToString(); + } + + /// + public static int[] splitIRI(string s) { + return (s == null) ? null : splitIRI(s, 0, s.Length, ParseMode.IRIStrict); + } + + /// + public static int[] splitIRI( + string s, + int offset, + int length, + ParseMode parseMode) { + if (s == null) { + return null; + } + if (s == null) { + throw new ArgumentNullException(nameof(s)); +} +if (offset < 0) { + throw new ArgumentException("offset (" + offset + + ") is less than 0"); +} +if (offset > s.Length) { + throw new ArgumentException("offset (" + offset + + ") is more than " + s.Length); +} +if (length < 0) { + throw new ArgumentException("length (" + length + + ") is less than 0"); +} +if (length > s.Length) { + throw new ArgumentException("length (" + length + + ") is more than " + s.Length); +} +if (s.Length - offset < length) { + throw new ArgumentException("s's length minus " + offset + " (" + + (s.Length - offset) + ") is less than " + length); +} + int[] retval = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + if (length == 0) { + retval[4] = 0; + retval[5] = 0; + return retval; + } + bool asciiOnly = parseMode == ParseMode.URILenient || parseMode == + ParseMode.URIStrict; + bool strict = parseMode == ParseMode.URIStrict || parseMode == + ParseMode.IRIStrict; + int index = offset; + int valueSLength = offset + length; + var scheme = false; + // scheme + while (index < valueSLength) { + int c = s[index]; + if (index > offset && c == ':') { + scheme = true; + retval[0] = offset; + retval[1] = index; + ++index; + break; + } + if (strict && index == offset && !((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z'))) { + break; + } + if (strict && index > offset && + !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && + c <= '9') || c == '+' || c == '-' || c == '.')) { + break; + } + if (!strict && (c == '#' || c == ':' || c == '?' || c == '/')) { + break; + } + ++index; + } + if (!scheme) { + index = offset; + } + var state = 0; + if (index + 2 <= valueSLength && s[index] == '/' && s[index + 1] == '/') { + // authority + // (index + 2, valueSLength) + index += 2; + int authorityStart = index; + retval[2] = authorityStart; + retval[3] = valueSLength; + state = 0; // userinfo + // Check for userinfo + while (index < valueSLength) { + int c = s[index]; + if (asciiOnly && c >= 0x80) { + return null; + } + if ((c & 0xfc00) == 0xd800 && index + 1 < valueSLength && + (s[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (s[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + if (parseMode == ParseMode.IRISurrogateLenient) { + c = 0xfffd; + } else { + return null; + } + } + if (c == '%' && (state == 0 || state == 1) && strict) { + // Percent encoded character (except in port) + if (index + 2 < valueSLength && isHexChar(s[index + 1]) && + isHexChar(s[index + 2])) { + index += 3; + continue; + } + return null; + } + if (state == 0) { // User info + if (c == '/' || c == '?' || c == '#') { + // not user info + state = 1; + index = authorityStart; + continue; + } + if (strict && c == '@') { + // is user info + ++index; + state = 1; + continue; + } + if (strict && isIUserInfoChar(c)) { + ++index; + if (index == valueSLength) { + // not user info + state = 1; + index = authorityStart; + continue; + } + } else { + // not user info + state = 1; + index = authorityStart; + continue; + } + } else if (state == 1) { // host + if (c == '/' || c == '?' || c == '#') { + // end of authority + retval[3] = index; + break; + } + if (!strict) { + ++index; + } else if (c == '[') { + ++index; + index = parseIPLiteral(s, index, valueSLength); + if (index < 0) { + return null; + } + continue; + } else if (c == ':') { + // port + state = 2; + ++index; + } else if (isIRegNameChar(c)) { + // is valid host name char + // (note: IPv4 addresses included + // in ireg-name) + ++index; + } else { + return null; + } + } else if (state == 2) { // Port + if (c == '/' || c == '?' || c == '#') { + // end of authority + retval[3] = index; + break; + } + if (c >= '0' && c <= '9') { + ++index; + } else { + return null; + } + } + } + } + var colon = false; + var segment = false; + bool fullyRelative = index == offset; + retval[4] = index; // path offsets + retval[5] = valueSLength; + state = 0; // IRI Path + while (index < valueSLength) { + // Get the next Unicode character + int c = s[index]; + if (asciiOnly && c >= 0x80) { + return null; + } + if ((c & 0xfc00) == 0xd800 && index + 1 < valueSLength && + (s[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (s[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + // error + return null; + } + if (c == '%' && strict) { + // Percent encoded character + if (index + 2 < valueSLength && isHexChar(s[index + 1]) && + isHexChar(s[index + 2])) { + index += 3; + continue; + } + return null; + } + if (state == 0) { // Path + if (c == ':' && fullyRelative) { + colon = true; + } else if (c == '/' && fullyRelative && !segment) { + // noscheme path can't have colon before slash + if (strict && colon) { + return null; + } + segment = true; + } + if (c == '?') { + retval[5] = index; + retval[6] = index + 1; + retval[7] = valueSLength; + state = 1; // move to query state + } else if (c == '#') { + retval[5] = index; + retval[8] = index + 1; + retval[9] = valueSLength; + state = 2; // move to fragment state + } else if (strict && !isIpchar(c)) { + return null; + } + ++index; + } else if (state == 1) { // Query + if (c == '#') { + retval[7] = index; + retval[8] = index + 1; + retval[9] = valueSLength; + state = 2; // move to fragment state + } else if (strict && !isIqueryChar(c)) { + return null; + } + ++index; + } else if (state == 2) { // Fragment + if (strict && !isIfragmentChar(c)) { + return null; + } + ++index; + } + } + if (strict && fullyRelative && colon && !segment) { + return null; // ex. "x@y:z" + } + return retval; + } + + /// + public static int[] splitIRI(string s, ParseMode parseMode) { + return (s == null) ? null : splitIRI(s, 0, s.Length, parseMode); + } + } +} diff --git a/PeterO/DataUtilities.cs b/PeterO/DataUtilities.cs new file mode 100644 index 00000000..afca1c42 --- /dev/null +++ b/PeterO/DataUtilities.cs @@ -0,0 +1,786 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.IO; +using System.Text; + +namespace PeterO { + /// + public static class DataUtilities { + private const int StreamedStringBufferLength = 4096; + + /// + public static string GetUtf8String(byte[] bytes, bool replace) { + if (bytes == null) { + throw new ArgumentNullException(nameof(bytes)); + } + var b = new StringBuilder(); + if (ReadUtf8FromBytes(bytes, 0, bytes.Length, b, replace) != 0) { + throw new ArgumentException("Invalid UTF-8"); + } + return b.ToString(); + } + + /// + public static int CodePointLength(string str) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + var i = 0; + var count = 0; + while (i < str.Length) { + int c = CodePointAt(str, i); + ++count; + i += (c >= 0x10000) ? 2 : 1; + } + return count; +} + + /// + public static string GetUtf8String( + byte[] bytes, + int offset, + int bytesCount, + bool replace) { + if (bytes == null) { + throw new ArgumentNullException(nameof(bytes)); + } + if (offset < 0) { + throw new ArgumentException("offset (" + offset + ") is less than " + + "0"); + } + if (offset > bytes.Length) { + throw new ArgumentException("offset (" + offset + ") is more than " + + bytes.Length); + } + if (bytesCount < 0) { + throw new ArgumentException("bytesCount (" + bytesCount + + ") is less than 0"); + } + if (bytesCount > bytes.Length) { + throw new ArgumentException("bytesCount (" + bytesCount + + ") is more than " + bytes.Length); + } + if (bytes.Length - offset < bytesCount) { + throw new ArgumentException("bytes's length minus " + offset + " (" + + (bytes.Length - offset) + ") is less than " + bytesCount); + } + var b = new StringBuilder(); + if (ReadUtf8FromBytes(bytes, offset, bytesCount, b, replace) != 0) { + throw new ArgumentException("Invalid UTF-8"); + } + return b.ToString(); + } + + /// + public static byte[] GetUtf8Bytes(string str, bool replace) { + return GetUtf8Bytes(str, replace, false); + } + + /// + public static byte[] GetUtf8Bytes( + string str, + bool replace, + bool lenientLineBreaks) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + if (!lenientLineBreaks && str.Length == 1) { + int c = str[0]; + if ((c & 0xf800) == 0xd800) { + if (replace) { + c = 0xfffd; +} else { + throw new ArgumentException("Unpaired surrogate code point"); +} + } + if (c <= 0x80) { + return new byte[] { (byte)c }; + } else if (c <= 0x7ff) { + return new byte[] { (byte)(0xc0 | ((c >> 6) & 0x1f)), + (byte)(0x80 | (c & 0x3f)) }; + } else { + return new byte[] { (byte)(0xe0 | ((c >> 12) & 0x0f)), + (byte)(0x80 | ((c >> 6) & 0x3f)), + (byte)(0x80 | (c & 0x3f)) }; + } + } else if (str.Length == 2) { + int c = str[0]; + int c2 = str[1]; + if ((c & 0xfc00) == 0xd800 && (c2 & 0xfc00) == 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + return new byte[] { (byte)(0xf0 | ((c >> 18) & 0x07)), + (byte)(0x80 | ((c >> 12) & 0x3f)), + (byte)(0x80 | ((c >> 6) & 0x3f)), + (byte)(0x80 | (c & 0x3f)) }; + } else if (!lenientLineBreaks && c <= 0x80 && c2 <= 0x80) { + return new byte[] { (byte)c, (byte)c2 }; + } + } + try { + using (var ms = new MemoryStream()) { + if (WriteUtf8(str, 0, str.Length, ms, replace, lenientLineBreaks) != + 0) { + throw new ArgumentException("Unpaired surrogate code point"); + } + return ms.ToArray(); + } + } catch (IOException ex) { + throw new ArgumentException("I/O error occurred", ex); + } + } + + /// + public static long GetUtf8Length(string str, bool replace) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + long size = 0; + for (var i = 0; i < str.Length; ++i) { + int c = str[i]; + if (c <= 0x7f) { + ++size; + } else if (c <= 0x7ff) { + size += 2; + } else if (c <= 0xd7ff || c >= 0xe000) { + size += 3; + } else if (c <= 0xdbff) { // UTF-16 leading surrogate + ++i; + if (i >= str.Length || str[i] < 0xdc00 || str[i] > 0xdfff) { + if (replace) { + size += 3; + --i; + } else { + return -1; + } + } else { + size += 4; + } + } else { + if (replace) { + size += 3; + } else { + return -1; + } + } + } + return size; + } + + /// + public static int CodePointBefore(string str, int index) { + return CodePointBefore(str, index, 0); + } + + /// + public static int CodePointBefore( + string str, + int index, + int surrogateBehavior) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + if (index <= 0) { + return -1; + } + if (index > str.Length) { + return -1; + } + int c = str[index - 1]; + if ((c & 0xfc00) == 0xdc00 && index - 2 >= 0 && + (str[index - 2] & 0xfc00) == 0xd800) { + // Get the Unicode code point for the surrogate pair + return 0x10000 + ((str[index - 2] - 0xd800) << 10) + (c - 0xdc00); + } + if ((c & 0xf800) == 0xd800) { + // unpaired surrogate + return (surrogateBehavior == 0) ? 0xfffd : ((surrogateBehavior == 1) ? + c : (-1)); + } + return c; + } + + /// + public static int CodePointAt(string str, int index) { + return CodePointAt(str, index, 0); + } + + /// + public static int CodePointAt( + string str, + int index, + int surrogateBehavior) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + if (index >= str.Length) { + return -1; + } + if (index < 0) { + return -1; + } + int c = str[index]; + if ((c & 0xfc00) == 0xd800 && index + 1 < str.Length && + (str[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (str[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + // unpaired surrogate + return (surrogateBehavior == 0) ? 0xfffd : ((surrogateBehavior == 1) ? + c : (-1)); + } + return c; + } + + /// + public static string ToLowerCaseAscii(string str) { + if (str == null) { + return null; + } + var len = str.Length; + var c = (char)0; + var hasUpperCase = false; + for (var i = 0; i < len; ++i) { + c = str[i]; + if (c >= 'A' && c <= 'Z') { + hasUpperCase = true; + break; + } + } + if (!hasUpperCase) { + return str; + } + var builder = new StringBuilder(); + for (var i = 0; i < len; ++i) { + c = str[i]; + if (c >= 'A' && c <= 'Z') { + builder.Append((char)(c + 0x20)); + } else { + builder.Append(c); + } + } + return builder.ToString(); + } + + /// + public static string ToUpperCaseAscii(string str) { + if (str == null) { + return null; + } + var len = str.Length; + var c = (char)0; + var hasLowerCase = false; + for (var i = 0; i < len; ++i) { + c = str[i]; + if (c >= 'a' && c <= 'z') { + hasLowerCase = true; + break; + } + } + if (!hasLowerCase) { + return str; + } + var builder = new StringBuilder(); + for (var i = 0; i < len; ++i) { + c = str[i]; + if (c >= 'a' && c <= 'z') { + builder.Append((char)(c - 0x20)); + } else { + builder.Append(c); + } + } + return builder.ToString(); + } + + /// + public static int CodePointCompare(string strA, string strB) { + if (strA == null) { + return (strB == null) ? 0 : -1; + } + if (strB == null) { + return 1; + } + int len, ca, cb; + len = Math.Min(strA.Length, strB.Length); + for (var i = 0; i < len; ++i) { + ca = strA[i]; + cb = strB[i]; + if (ca == cb) { + // normal code units and illegal surrogates + // are treated as single code points + if ((ca & 0xf800) != 0xd800) { + continue; + } + var incindex = false; + if (i + 1 < strA.Length && (strA[i + 1] & 0xfc00) == 0xdc00) { + ca = 0x10000 + ((ca - 0xd800) << 10) + (strA[i + 1] - 0xdc00); + incindex = true; + } + if (i + 1 < strB.Length && (strB[i + 1] & 0xfc00) == 0xdc00) { + cb = 0x10000 + ((cb - 0xd800) << 10) + (strB[i + 1] - 0xdc00); + incindex = true; + } + if (ca != cb) { + return ca - cb; + } + if (incindex) { + ++i; + } + } else { + if ((ca & 0xf800) != 0xd800 && (cb & 0xf800) != 0xd800) { + return ca - cb; + } + if ((ca & 0xfc00) == 0xd800 && i + 1 < strA.Length && + (strA[i + 1] & 0xfc00) == 0xdc00) { + ca = 0x10000 + ((ca - 0xd800) << 10) + (strA[i + 1] - 0xdc00); + } + if ((cb & 0xfc00) == 0xd800 && i + 1 < strB.Length && + (strB[i + 1] & 0xfc00) == 0xdc00) { + cb = 0x10000 + ((cb - 0xd800) << 10) + (strB[i + 1] - 0xdc00); + } + return ca - cb; + } + } + return (strA.Length == strB.Length) ? 0 : ((strA.Length < strB.Length) ? + -1 : 1); + } + + /// + public static int WriteUtf8( + string str, + int offset, + int length, + Stream stream, + bool replace) { + return WriteUtf8(str, offset, length, stream, replace, false); + } + + /// + public static int WriteUtf8( + string str, + int offset, + int length, + Stream stream, + bool replace, + bool lenientLineBreaks) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + if (offset < 0) { + throw new ArgumentException("offset (" + offset + ") is less than " + + "0"); + } + if (offset > str.Length) { + throw new ArgumentException("offset (" + offset + ") is more than " + + str.Length); + } + if (length < 0) { + throw new ArgumentException("length (" + length + ") is less than " + + "0"); + } + if (length > str.Length) { + throw new ArgumentException("length (" + length + ") is more than " + + str.Length); + } + if (str.Length - offset < length) { + throw new ArgumentException("str.Length minus offset (" + + (str.Length - offset) + ") is less than " + length); + } + int endIndex, c; + byte[] bytes; + var retval = 0; + bytes = new byte[StreamedStringBufferLength]; + var byteIndex = 0; + endIndex = offset + length; + for (int index = offset; index < endIndex; ++index) { + c = str[index]; + if (c <= 0x7f) { + if (lenientLineBreaks) { + if (c == 0x0d && (index + 1 >= endIndex || str[index + 1] != + 0x0a)) { + // bare CR, convert to CRLF + if (byteIndex + 2 > StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = 0x0d; + bytes[byteIndex++] = 0x0a; + continue; + } else if (c == 0x0d) { + // CR-LF pair + if (byteIndex + 2 > StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = 0x0d; + bytes[byteIndex++] = 0x0a; + ++index; + continue; + } + if (c == 0x0a) { + // bare LF, convert to CRLF + if (byteIndex + 2 > StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = 0x0d; + bytes[byteIndex++] = 0x0a; + continue; + } + } + if (byteIndex >= StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = (byte)c; + } else if (c <= 0x7ff) { + if (byteIndex + 2 > StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = (byte)(0xc0 | ((c >> 6) & 0x1f)); + bytes[byteIndex++] = (byte)(0x80 | (c & 0x3f)); + } else { + if ((c & 0xfc00) == 0xd800 && index + 1 < endIndex && + (str[index + 1] & 0xfc00) == 0xdc00) { + // Get the Unicode code point for the surrogate pair + c = 0x10000 + ((c - 0xd800) << 10) + (str[index + 1] - 0xdc00); + ++index; + } else if ((c & 0xf800) == 0xd800) { + // unpaired surrogate + if (!replace) { + retval = -1; + break; // write bytes read so far + } + c = 0xfffd; + } + if (c <= 0xffff) { + if (byteIndex + 3 > StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = (byte)(0xe0 | ((c >> 12) & 0x0f)); + bytes[byteIndex++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + bytes[byteIndex++] = (byte)(0x80 | (c & 0x3f)); + } else { + if (byteIndex + 4 > StreamedStringBufferLength) { + // Write bytes retrieved so far + stream.Write(bytes, 0, byteIndex); + byteIndex = 0; + } + bytes[byteIndex++] = (byte)(0xf0 | ((c >> 18) & 0x07)); + bytes[byteIndex++] = (byte)(0x80 | ((c >> 12) & 0x3f)); + bytes[byteIndex++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + bytes[byteIndex++] = (byte)(0x80 | (c & 0x3f)); + } + } + } + stream.Write(bytes, 0, byteIndex); + return retval; + } + + /// + public static int WriteUtf8(string str, Stream stream, bool replace) { + if (str == null) { + throw new ArgumentNullException(nameof(str)); + } + return WriteUtf8(str, 0, str.Length, stream, replace); + } + + /// + public static int ReadUtf8FromBytes( + byte[] data, + int offset, + int bytesCount, + StringBuilder builder, + bool replace) { + if (data == null) { + throw new ArgumentNullException(nameof(data)); + } + if (offset < 0) { + throw new ArgumentException("offset (" + offset + ") is less than " + + "0"); + } + if (offset > data.Length) { + throw new ArgumentException("offset (" + offset + ") is more than " + + data.Length); + } + if (bytesCount < 0) { + throw new ArgumentException("bytesCount (" + bytesCount + + ") is less than 0"); + } + if (bytesCount > data.Length) { + throw new ArgumentException("bytesCount (" + bytesCount + + ") is more than " + data.Length); + } + if (data.Length - offset < bytesCount) { + throw new ArgumentException("data.Length minus offset (" + + (data.Length - offset) + ") is less than " + bytesCount); + } + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + var cp = 0; + var bytesSeen = 0; + var bytesNeeded = 0; + var lower = 0x80; + var upper = 0xbf; + int pointer, endpointer, b; + pointer = offset; + endpointer = offset + bytesCount; + while (pointer < endpointer) { + b = data[pointer] & (int)0xff; + ++pointer; + if (bytesNeeded == 0) { + if ((b & 0x7f) == b) { + builder.Append((char)b); + } else if (b >= 0xc2 && b <= 0xdf) { + bytesNeeded = 1; + cp = (b - 0xc0) << 6; + } else if (b >= 0xe0 && b <= 0xef) { + lower = (b == 0xe0) ? 0xa0 : 0x80; + upper = (b == 0xed) ? 0x9f : 0xbf; + bytesNeeded = 2; + cp = (b - 0xe0) << 12; + } else if (b >= 0xf0 && b <= 0xf4) { + lower = (b == 0xf0) ? 0x90 : 0x80; + upper = (b == 0xf4) ? 0x8f : 0xbf; + bytesNeeded = 3; + cp = (b - 0xf0) << 18; + } else { + if (replace) { + builder.Append((char)0xfffd); + } else { + return -1; + } + } + continue; + } + if (b < lower || b > upper) { + cp = bytesNeeded = bytesSeen = 0; + lower = 0x80; + upper = 0xbf; + if (replace) { + --pointer; + builder.Append((char)0xfffd); + continue; + } + return -1; + } else { + lower = 0x80; + upper = 0xbf; + ++bytesSeen; + cp += (b - 0x80) << (6 * (bytesNeeded - bytesSeen)); + if (bytesSeen != bytesNeeded) { + continue; + } + int ret, ch, lead, trail; + ret = cp; + cp = 0; + bytesSeen = 0; + bytesNeeded = 0; + if (ret <= 0xffff) { + builder.Append((char)ret); + } else { + ch = ret - 0x10000; + lead = (ch >> 10) + 0xd800; + trail = (ch & 0x3ff) + 0xdc00; + builder.Append((char)lead); + builder.Append((char)trail); + } + } + } + if (bytesNeeded != 0) { + if (replace) { + builder.Append((char)0xfffd); + } else { + return -1; + } + } + return 0; + } + + /// + public static string ReadUtf8ToString(Stream stream) { + return ReadUtf8ToString(stream, -1, true); + } + + /// + public static string ReadUtf8ToString( + Stream stream, + int bytesCount, + bool replace) { + var builder = new StringBuilder(); + if (DataUtilities.ReadUtf8(stream, bytesCount, builder, replace) == -1) { + throw new IOException( + "Unpaired surrogate code point found.", + new ArgumentException("Unpaired surrogate code point found.")); + } + return builder.ToString(); + } + + /// + public static int ReadUtf8( + Stream stream, + int bytesCount, + StringBuilder builder, + bool replace) { + if (stream == null) { + throw new ArgumentNullException(nameof(stream)); + } + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + int b; + var cp = 0; + var bytesSeen = 0; + var bytesNeeded = 0; + var lower = 0x80; + var upper = 0xbf; + var pointer = 0; + while (pointer < bytesCount || bytesCount < 0) { + b = stream.ReadByte(); + if (b < 0) { + if (bytesNeeded != 0) { + bytesNeeded = 0; + if (replace) { + builder.Append((char)0xfffd); + if (bytesCount >= 0) { + return -2; + } + break; // end of stream + } + return -1; + } + if (bytesCount >= 0) { + return -2; + } + break; // end of stream + } + if (bytesCount > 0) { + ++pointer; + } + if (bytesNeeded == 0) { + if ((b & 0x7f) == b) { + builder.Append((char)b); + } else if (b >= 0xc2 && b <= 0xdf) { + bytesNeeded = 1; + cp = (b - 0xc0) << 6; + } else if (b >= 0xe0 && b <= 0xef) { + lower = (b == 0xe0) ? 0xa0 : 0x80; + upper = (b == 0xed) ? 0x9f : 0xbf; + bytesNeeded = 2; + cp = (b - 0xe0) << 12; + } else if (b >= 0xf0 && b <= 0xf4) { + lower = (b == 0xf0) ? 0x90 : 0x80; + upper = (b == 0xf4) ? 0x8f : 0xbf; + bytesNeeded = 3; + cp = (b - 0xf0) << 18; + } else { + if (replace) { + builder.Append((char)0xfffd); + } else { + return -1; + } + } + continue; + } + if (b < lower || b > upper) { + cp = bytesNeeded = bytesSeen = 0; + lower = 0x80; + upper = 0xbf; + if (replace) { + builder.Append((char)0xfffd); + // "Read" the last byte again + if (b < 0x80) { + builder.Append((char)b); + } else if (b >= 0xc2 && b <= 0xdf) { + bytesNeeded = 1; + cp = (b - 0xc0) << 6; + } else if (b >= 0xe0 && b <= 0xef) { + lower = (b == 0xe0) ? 0xa0 : 0x80; + upper = (b == 0xed) ? 0x9f : 0xbf; + bytesNeeded = 2; + cp = (b - 0xe0) << 12; + } else if (b >= 0xf0 && b <= 0xf4) { + lower = (b == 0xf0) ? 0x90 : 0x80; + upper = (b == 0xf4) ? 0x8f : 0xbf; + bytesNeeded = 3; + cp = (b - 0xf0) << 18; + } else { + builder.Append((char)0xfffd); + } + continue; + } + return -1; + } else { + lower = 0x80; + upper = 0xbf; + ++bytesSeen; + cp += (b - 0x80) << (6 * (bytesNeeded - bytesSeen)); + if (bytesSeen != bytesNeeded) { + continue; + } + int ret, ch, lead, trail; + ret = cp; + cp = 0; + bytesSeen = 0; + bytesNeeded = 0; + if (ret <= 0xffff) { + builder.Append((char)ret); + } else { + ch = ret - 0x10000; + lead = (ch >> 10) + 0xd800; + trail = (ch & 0x3ff) + 0xdc00; + builder.Append((char)lead); + builder.Append((char)trail); + } + } + } + if (bytesNeeded != 0) { + if (replace) { + builder.Append((char)0xfffd); + } else { + return -1; + } + } + return 0; + } + } +} diff --git a/PeterO/DebugUtility.cs b/PeterO/DebugUtility.cs new file mode 100644 index 00000000..5eb898da --- /dev/null +++ b/PeterO/DebugUtility.cs @@ -0,0 +1,41 @@ +/* +Written by Peter O. in 2014-2018. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ + #if DEBUG +using System; +using System.Reflection; + +namespace PeterO { + internal static class DebugUtility { + private static MethodInfo GetTypeMethod( + Type t, + string name, + Type[] parameters) { +#if NET40 || NET20 + return t.GetMethod(name, parameters); +#else +{ + return t?.GetRuntimeMethod(name, parameters); +} +#endif + } + + public static void Log(string str) { + Type type = Type.GetType("System.Console"); + var types = new[] { typeof(string) }; + var typeMethod = GetTypeMethod(type, "WriteLine", types); + if (typeMethod != null)typeMethod.Invoke( + type, + new object[] { str }); + } + + public static void Log(string format, params object[] args) { + Log(String.Format(format, args)); + } + } +} +#endif diff --git a/PeterO/ExtendedDecimal.cs b/PeterO/ExtendedDecimal.cs new file mode 100644 index 00000000..5cfeec0e --- /dev/null +++ b/PeterO/ExtendedDecimal.cs @@ -0,0 +1,229 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using System.Text; +using PeterO.Numbers; + +namespace PeterO { + /// +[Obsolete( + "Use EDecimal from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public sealed class ExtendedDecimal : IComparable, + IEquatable { + /// + public BigInteger Exponent { + get { + return new BigInteger(this.Ed.Exponent); + } + } + + /// + public BigInteger UnsignedMantissa { + get { + return new BigInteger(this.Ed.UnsignedMantissa); + } + } + + /// + public BigInteger Mantissa { + get { + return new BigInteger(this.Ed.Mantissa); + } + } + + internal static ExtendedDecimal ToLegacy(EDecimal ei) { + return new ExtendedDecimal(ei); + } + + internal static EDecimal FromLegacy(ExtendedDecimal bei) { + return bei.Ed; + } + + #region Equals and GetHashCode implementation + + /// + [Obsolete("Use EDecimal from PeterO.Numbers/com.upokecenter.numbers.")] + public bool Equals(ExtendedDecimal other) { + return (other == null) ? false : this.Ed.Equals(other.Ed); + } + + /// + public override bool Equals(object obj) { + var bi = obj as ExtendedDecimal; + return (bi == null) ? false : this.Ed.Equals(bi.Ed); + } + + /// + public override int GetHashCode() { + return this.Ed.GetHashCode(); + } + #endregion + private readonly EDecimal ed; + + internal ExtendedDecimal(EDecimal ed) { + if (ed == null) { + throw new ArgumentNullException(nameof(ed)); + } + this.ed = ed; + } + + /// + public static ExtendedDecimal Create( + BigInteger mantissa, + BigInteger exponent) { + if (mantissa == null) { + throw new ArgumentNullException(nameof(mantissa)); + } + if (exponent == null) { + throw new ArgumentNullException(nameof(exponent)); + } + return new ExtendedDecimal(EDecimal.Create(mantissa.Ei, exponent.Ei)); + } + + /// + public static ExtendedDecimal FromString(string str) { + return new ExtendedDecimal(EDecimal.FromString(str)); + } + + /// + public float ToSingle() { + return this.Ed.ToSingle(); + } + + /// + public double ToDouble() { + return this.Ed.ToDouble(); + } + + /// + public override string ToString() { + return this.Ed.ToString(); + } + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", + "CA2104", Justification = "ExtendedDecimal is immutable")] +#endif + [Obsolete("Use EDecimal from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedDecimal One = + ExtendedDecimal.Create(BigInteger.One, BigInteger.Zero); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", + "CA2104", Justification = "ExtendedDecimal is immutable")] +#endif + [Obsolete("Use EDecimal from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedDecimal Zero = + ExtendedDecimal.Create(BigInteger.Zero, BigInteger.Zero); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", + "CA2104", Justification = "ExtendedDecimal is immutable")] +#endif + public static readonly ExtendedDecimal NegativeZero = + new ExtendedDecimal(EDecimal.NegativeZero); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", + "CA2104", Justification = "ExtendedDecimal is immutable")] +#endif + public static readonly ExtendedDecimal Ten = + new ExtendedDecimal(EDecimal.Ten); + + //---------------------------------------------------------------- + + /// + public static readonly ExtendedDecimal NaN = + new ExtendedDecimal(EDecimal.NaN); + + /// + public static readonly ExtendedDecimal SignalingNaN = + new ExtendedDecimal(EDecimal.SignalingNaN); + + /// + public static readonly ExtendedDecimal PositiveInfinity = + new ExtendedDecimal(EDecimal.PositiveInfinity); + + /// + public static readonly ExtendedDecimal NegativeInfinity = + new ExtendedDecimal(EDecimal.NegativeInfinity); + + /// + public bool IsNaN() { + return this.Ed.IsNaN(); + } + + /// + public bool IsInfinity() { + return this.Ed.IsInfinity(); + } + + /// + [Obsolete("Use EDecimal from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsNegative { + get { + return this.Ed.IsNegative; + } + } + + /// + [Obsolete("Use EDecimal from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsQuietNaN() { + return this.Ed.IsQuietNaN(); + } + + /// + public int CompareTo(ExtendedDecimal other) { + return this.Ed.CompareTo(other == null ? null : other.Ed); + } + + /// + [Obsolete("Use EDecimal from PeterO.Numbers/com.upokecenter.numbers.")] + public int Sign { + get { + return this.Ed.Sign; + } + } + + internal EDecimal Ed { + get { + return this.ed; + } + } + } +} diff --git a/PeterO/ExtendedFloat.cs b/PeterO/ExtendedFloat.cs new file mode 100644 index 00000000..83adeff3 --- /dev/null +++ b/PeterO/ExtendedFloat.cs @@ -0,0 +1,284 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO { + /// +[Obsolete( + "Use EFloat from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public sealed class ExtendedFloat : IComparable, + IEquatable { + private readonly EFloat ef; + + internal ExtendedFloat(EFloat ef) { + this.ef = ef; + } + + /// + public BigInteger Exponent { + get { + return new BigInteger(this.Ef.Exponent); + } + } + + /// + public BigInteger UnsignedMantissa { + get { + return new BigInteger(this.Ef.UnsignedMantissa); + } + } + + /// + public BigInteger Mantissa { + get { + return new BigInteger(this.Ef.Mantissa); + } + } + + internal static ExtendedFloat ToLegacy(EFloat ei) { + return new ExtendedFloat(ei); + } + + internal static EFloat FromLegacy(ExtendedFloat bei) { + return bei.Ef; + } + + #region Equals and GetHashCode implementation + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool EqualsInternal(ExtendedFloat otherValue) { + if (otherValue == null) { + throw new ArgumentNullException(nameof(otherValue)); + } + return this.Ef.EqualsInternal(otherValue.Ef); + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool Equals(ExtendedFloat other) { + if (other == null) { + throw new ArgumentNullException(nameof(other)); + } + return this.Ef.Equals(other.Ef); + } + + /// + public override bool Equals(object obj) { + var bi = obj as ExtendedFloat; + return (bi == null) ? false : this.Ef.Equals(bi.Ef); + } + + /// + public override int GetHashCode() { + return this.Ef.GetHashCode(); + } + #endregion + + /// + public static ExtendedFloat Create(int mantissaSmall, int exponentSmall) { + return new ExtendedFloat(EFloat.Create(mantissaSmall, exponentSmall)); + } + + /// + public static ExtendedFloat Create( + BigInteger mantissa, + BigInteger exponent) { + if (mantissa == null) { + throw new ArgumentNullException(nameof(mantissa)); + } + if (exponent == null) { + throw new ArgumentNullException(nameof(exponent)); + } + return new ExtendedFloat(EFloat.Create(mantissa.Ei, exponent.Ei)); + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public static ExtendedFloat FromString( + string str, + int offset, + int length, + PrecisionContext ctx) { + try { + return new ExtendedFloat( + EFloat.FromString( + str, + offset, + length, + ctx == null ? null : ctx.Ec)); + } catch (ETrapException ex) { + throw TrapException.Create(ex); + } + } + + /// + public static ExtendedFloat FromString(string str) { + return new ExtendedFloat(EFloat.FromString(str)); + } + + /// + public override string ToString() { + return this.Ef.ToString(); + } + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "ExtendedFloat is immutable")] +#endif + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedFloat One = + new ExtendedFloat(EFloat.One); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "ExtendedFloat is immutable")] +#endif + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedFloat Zero = + new ExtendedFloat(EFloat.Zero); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "ExtendedFloat is immutable")] +#endif + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedFloat NegativeZero = + new ExtendedFloat(EFloat.NegativeZero); + + /// +#if CODE_ANALYSIS + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Microsoft.Security", + "CA2104", + Justification = "ExtendedFloat is immutable")] +#endif + + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedFloat Ten = + new ExtendedFloat(EFloat.Ten); + + //---------------------------------------------------------------- + + /// + public static readonly ExtendedFloat NaN = + new ExtendedFloat(EFloat.NaN); + + /// + public static readonly ExtendedFloat SignalingNaN = + new ExtendedFloat(EFloat.SignalingNaN); + + /// + public static readonly ExtendedFloat PositiveInfinity = + new ExtendedFloat(EFloat.PositiveInfinity); + + /// + public static readonly ExtendedFloat NegativeInfinity = + new ExtendedFloat(EFloat.NegativeInfinity); + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsNegativeInfinity() { + return this.Ef.IsNegativeInfinity(); + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsPositiveInfinity() { + return this.Ef.IsPositiveInfinity(); + } + + /// + public bool IsNaN() { + return this.Ef.IsNaN(); + } + + /// + public bool IsInfinity() { + return this.Ef.IsInfinity(); + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsNegative { + get { + return this.Ef.IsNegative; + } + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsQuietNaN() { + return this.Ef.IsQuietNaN(); + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsSignalingNaN() { + return this.Ef.IsSignalingNaN(); + } + + /// + public int CompareTo(ExtendedFloat other) { + return this.Ef.CompareTo(other == null ? null : other.Ef); + } + + /// + [Obsolete("Use EFloat from PeterO.Numbers/com.upokecenter.numbers.")] + public int Sign { + get { + return this.Ef.Sign; + } + } + + internal EFloat Ef { + get { + return this.ef; + } + } + } +} diff --git a/PeterO/ExtendedRational.cs b/PeterO/ExtendedRational.cs new file mode 100644 index 00000000..111756d8 --- /dev/null +++ b/PeterO/ExtendedRational.cs @@ -0,0 +1,211 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO { + /// +[Obsolete( + "Use ERational from PeterO.Numbers/com.upokecenter.numbers and the output of this class's ToString method.")] + public sealed class ExtendedRational : IComparable, + IEquatable { + /// + [Obsolete("Use ERational from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedRational NaN = + new ExtendedRational(ERational.NaN); + + /// + public static readonly ExtendedRational NegativeInfinity = new + ExtendedRational(ERational.NegativeInfinity); + + /// + public static readonly ExtendedRational NegativeZero = + new ExtendedRational(ERational.NegativeZero); + + /// + public static readonly ExtendedRational One = + FromBigIntegerInternal(BigInteger.One); + + /// + public static readonly ExtendedRational PositiveInfinity = new + ExtendedRational(ERational.PositiveInfinity); + + /// + [Obsolete("Use ERational from PeterO.Numbers/com.upokecenter.numbers.")] + public static readonly ExtendedRational SignalingNaN = new + ExtendedRational(ERational.SignalingNaN); + + /// + public static readonly ExtendedRational Ten = + FromBigIntegerInternal(BigInteger.valueOf(10)); + + /// + public static readonly ExtendedRational Zero = + FromBigIntegerInternal(BigInteger.Zero); + + private readonly ERational er; + + /// + public ExtendedRational(BigInteger numerator, BigInteger denominator) { + this.er = new ERational(numerator.Ei, denominator.Ei); + } + + internal ExtendedRational(ERational er) { + if (er == null) { + throw new ArgumentNullException(nameof(er)); + } + this.er = er; + } + + /// + public BigInteger Denominator { + get { + return new BigInteger(this.Er.Denominator); + } + } + + /// + [Obsolete("Use ERational from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsFinite { + get { + return this.Er.IsFinite; + } + } + + /// + public bool IsNegative { + get { + return this.Er.IsNegative; + } + } + + /// + [Obsolete("Use ERational from PeterO.Numbers/com.upokecenter.numbers.")] + public bool IsZero { + get { + return this.Er.IsZero; + } + } + + /// + public BigInteger Numerator { + get { + return new BigInteger(this.Er.Numerator); + } + } + + /// + [Obsolete("Use ERational from PeterO.Numbers/com.upokecenter.numbers.")] + public int Sign { + get { + return this.Er.Sign; + } + } + + /// + public BigInteger UnsignedNumerator { + get { + return new BigInteger(this.Er.UnsignedNumerator); + } + } + + internal ERational Er { + get { + return this.er; + } + } + + /// + public static ExtendedRational Create( + int numeratorSmall, + int denominatorSmall) { + return new ExtendedRational( + ERational.Create( + numeratorSmall, + denominatorSmall)); + } + + /// + public static ExtendedRational Create( + BigInteger numerator, + BigInteger denominator) { + if (numerator == null) { + throw new ArgumentNullException(nameof(numerator)); + } + if (denominator == null) { + throw new ArgumentNullException(nameof(denominator)); + } + return new ExtendedRational( + ERational.Create( + numerator.Ei, + denominator.Ei)); + } + + /// + public override string ToString() { + return this.Er.ToString(); + } + + internal static ERational FromLegacy(ExtendedRational bei) { + return bei.Er; + } + + internal static ExtendedRational ToLegacy(ERational ei) { + return new ExtendedRational(ei); + } + + private static ExtendedRational FromBigIntegerInternal(BigInteger bigint) { + return new ExtendedRational(ERational.FromEInteger(bigint.Ei)); + } + + /// + public int CompareTo(ExtendedRational other) { + return this.Er.CompareTo(other == null ? null : other.Er); + } + + /// + public bool Equals(ExtendedRational other) { + return this.Er.Equals(other == null ? null : other.Er); + } + + /// + public override bool Equals(object obj) { + var other = obj as ExtendedRational; + return this.Er.Equals(other == null ? null : other.Er); + } + + /// + public override int GetHashCode() { + return this.Er.GetHashCode(); + } + } +} diff --git a/PeterO/PrecisionContext.cs b/PeterO/PrecisionContext.cs new file mode 100644 index 00000000..c87be3bd --- /dev/null +++ b/PeterO/PrecisionContext.cs @@ -0,0 +1,45 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO { + /// + [Obsolete("Use EContext from PeterO.Numbers/com.upokecenter.numbers.")] + public class PrecisionContext { + private readonly EContext ec; + + internal EContext Ec { + get { + return this.ec; + } + } + + internal PrecisionContext(EContext ec) { + this.ec = ec; + } + + /// + public PrecisionContext( + int precision, + Rounding rounding, + int exponentMinSmall, + int exponentMaxSmall, + bool clampNormalExponents) { + throw new NotSupportedException("This class is now obsolete."); + } + + /// + public override string ToString() { + return String.Empty; + } + } +} diff --git a/PeterO/Rounding.cs b/PeterO/Rounding.cs new file mode 100644 index 00000000..41000a3f --- /dev/null +++ b/PeterO/Rounding.cs @@ -0,0 +1,60 @@ +/* +Written by Peter O. in 2013. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ + +using System; + +namespace PeterO { + /// + [Obsolete("Use ERounding from PeterO.Numbers/com.upokecenter.numbers.")] + public enum Rounding { + /// + Up, + + /// + Down, + + /// + Ceiling, + + /// + Floor, + + /// + HalfUp, + + /// + HalfDown, + + /// + HalfEven, + + /// + Unnecessary, + + /// + ZeroFiveUp, + + /// + Odd, + + /// + OddOrZeroFiveUp + } +} diff --git a/PeterO/TrapException.cs b/PeterO/TrapException.cs new file mode 100644 index 00000000..f4b1e128 --- /dev/null +++ b/PeterO/TrapException.cs @@ -0,0 +1,69 @@ +/* +Written by Peter O. in 2014. +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +If you like this, you should donate to Peter O. +at: http://peteroupc.github.io/ + */ +using System; +using PeterO.Numbers; + +namespace PeterO { + /// + [Obsolete( + "Use ETrapException from PeterO.Numbers/com.upokecenter.numbers.")] + public class TrapException : ArithmeticException { + private ETrapException ete; + + /// + public PrecisionContext Context { get { + return new PrecisionContext(this.ete.Context); +} } + + /// + public Object Result { get { + return this.ete.Result; +} } + + /// + public int Error { get { + return this.ete.Error; +} } + + private TrapException() : base() { + } + + internal static TrapException Create(ETrapException ete) { + var ex = new TrapException(); + ex.ete = ete; + return ex; + } + + /// + public TrapException(int flag, PrecisionContext ctx, Object result) : + base(String.Empty) { + Object wrappedResult = result; + var ed = result as EDecimal; + var er = result as ERational; + var ef = result as EFloat; + if (ed != null) { + wrappedResult = new ExtendedDecimal(ed); +} + if (er != null) { + wrappedResult = new ExtendedRational(er); +} + if (ef != null) { + wrappedResult = new ExtendedFloat(ef); +} + this.ete = new ETrapException( + flag, + ctx == null ? null : ctx.Ec, + wrappedResult); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..745baeb8 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,2 @@ +using System; +[assembly: CLSCompliant(true)] diff --git a/docs.xml b/docs.xml new file mode 100644 index 00000000..62390469 --- /dev/null +++ b/docs.xml @@ -0,0 +1,6113 @@ + + +An array of bytes that grows as needed. + + + +Initializes a new instance of the class with a default buffer size. + + + +Initializes a new instance of the class with the given initial buffer size. + + The parameter + + is a 32-bit signed integer. + + + +Offers a fast way to reset the length of the array writer's data to 0. + + + +Generates an array of all bytes written so far to it. + + A byte array. + + + +Writes a series of bytes to the array. + + Byte array containing the data to write. + + A zero-based index showing where the desired portion of + + begins. + + The number of elements in the desired portion of + + (but not more than + + 's length). + + The parameter + + is null. + + Either + + or + + is less than 0 or greater than + + 's length, or + + ' s length minus + + is less than + + . + + + +Writes an 8-bit byte to the array. + + An integer containing the byte to write. Only the lower 8 bits of this + value will be used. + + + +This class is largely obsolete. It will be replaced by a new version + of this class in a different namespace/package and library, called + PeterO.Numbers.EInteger + in the + + PeterO.Numbers + + library (in .NET), or + com.upokecenter.numbers.EInteger + in the + + com.github.peteroupc/numbers + + artifact (in Java). This new class can be used in the + CBORObject.FromObject(object) + method (by including the new library in your code, among other + things). + + An arbitrary-precision integer. + Thread safety: + Instances of this class are immutable, so they are inherently safe for + use by multiple threads. Multiple instances of this object with the same + value are interchangeable, but they should be compared using the + "Equals" method rather than the "==" operator. + + + + + + Compares this value to another. + The parameter + + is an arbitrary-precision integer. + Less than 0 if this value is less than, 0 if equal to, or greater than 0 + if greater than the other value. + + + + + + Returns whether this number's value equals another number's value. + An arbitrary-precision integer. + true + if this number's value equals another number's value; otherwise, false + . + + + + + + Determines whether this object and another object are equal. + The parameter + + is an arbitrary object. + true + if this object and another object are equal; otherwise, false + . + + + + + + Returns the hash code for this instance. No application or process IDs + are used in the hash code calculation. + A 32-bit signed integer. + + + + + + BigInteger for the number one. + + + + + + Gets the arbitrary-precision integer for one. + The arbitrary-precision integer for one. + + + + + + Converts this object to a text string in base 10. + A string representation of this object. If negative, the string will + begin with a minus sign ("-", U+002D). The string will use the basic + digits 0 to 9 (U+0030 to U+0039). + + + + + + Gets the arbitrary-precision integer for zero. + The arbitrary-precision integer for zero. + + + + + + Finds the minimum number of bits needed to represent this object's value, + except for its sign. If the value is negative, finds the number of bits in + a value equal to this object's absolute value minus 1. + The number of bits in this object's value. Returns 0 if this object's + value is 0 or negative 1. + + + + + + Initializes an arbitrary-precision integer from an array of bytes. + A byte array consisting of the two's-complement form of the + arbitrary-precision integer to create. The last byte contains the lowest + 8-bits, the next-to-last contains the next lowest 8 bits, and so on. To + encode negative numbers, take the absolute value of the number, subtract + by 1, encode the number into bytes, XOR each byte, and if the + most-significant bit of the first byte isn't set, add an additional byte + at the start with the value 255. For little-endian, the byte order is + reversed from the byte order just discussed. + If true, the byte order is little-endian, or least-significant-byte + first. If false, the byte order is big-endian, or most-significant-byte + first. + An arbitrary-precision integer. Returns 0 if the byte array's length is + 0. + The parameter + + is null. + + + + + + Converts a string to an arbitrary-precision integer. + A text string. The string must contain only characters allowed by the + given radix, except that it may start with a minus sign ("-", U+002D) to + indicate a negative number. The string is not allowed to contain white + space characters, including spaces. + A base from 2 to 36. Depending on the radix, the string can use the basic + digits 0 to 9 (U+0030 to U+0039) and then the basic letters A to Z (U+0041 + to U+005A). For example, 0-9 in radix 10, and 0-9, then A-F in radix 16. + An arbitrary-precision integer with the same value as given in the + string. + The parameter + + is null. + The parameter + + is less than 2 or greater than 36. + The string is empty or in an invalid format. + + + + + + Converts a string to an arbitrary-precision integer. + A text string. The string must contain only basic digits 0 to 9 (U+0030 + to U+0039), except that it may start with a minus sign ("-", U+002D) to + indicate a negative number. The string is not allowed to contain white + space characters, including spaces. + An arbitrary-precision integer with the same value as given in the + string. + The parameter + + is null. + The parameter + + is in an invalid format. + + + + + + Returns a byte array of this object's value. The byte array will take the + form of the number's two' s-complement representation, using the fewest + bytes necessary to represent its value unambiguously. If this value is + negative, the bits that appear "before" the most significant bit of the + number will be all ones. + If true, the least significant bits will appear first. + A byte array. If this value is 0, returns a byte array with the single + element 0. + + + + + + Generates a string representing the value of this object, in the given + radix. + A radix from 2 through 36. For example, to generate a hexadecimal + (base-16) string, specify 16. To generate a decimal (base-10) string, + specify 10. + A string representing the value of this object. If this value is 0, + returns "0". If negative, the string will begin with a hyphen/minus ("-"). + Depending on the radix, the string will use the basic digits 0 to 9 + (U+0030 to U+0039) and then the basic letters A to Z (U+0041 to U+005A). + For example, 0-9 in radix 10, and 0-9, then A-F in radix 16. + The parameter "index" is less than 0, "endIndex" is less than 0, or + either is greater than the string's length, or "endIndex" is less than + "index" ; or radix is less than 2 or greater than 36. + + + + + + Converts a 64-bit signed integer to a big integer. + The parameter + + is a 64-bit signed integer. + An arbitrary-precision integer with the same value as the 64-bit number. + + + + + + Contains methods useful for reading and writing data, with a focus on + CBOR. + + + + + + Parses a number whose format follows the JSON specification. See + #ParseJSONNumber(String, integersOnly, parseOnly) for more information. + A string to parse. The string is not allowed to contain white space + characters, including spaces. + A CBOR object that represents the parsed number. Returns positive zero if + the number is a zero that starts with a minus sign (such as "-0" or + "-0.0"). Returns null if the parsing fails, including if the string is + null or empty. + + + + + + Parses a number whose format follows the JSON specification (RFC 8259). + Roughly speaking, a valid number consists of an optional minus sign, one + or more basic digits (starting with 1 to 9 unless the only digit is 0), an + optional decimal point (".", full stop) with one or more basic digits, and + an optional letter E or e with an optional plus or minus sign and one or + more basic digits (the exponent). + A string to parse. The string is not allowed to contain white space + characters, including spaces. + If true, no decimal points or exponents are allowed in the string. + If true, only positive numbers are allowed (the leading minus is + disallowed). + A CBOR object that represents the parsed number. Returns positive zero if + the number is a zero that starts with a minus sign (such as "-0" or + "-0.0"). Returns null if the parsing fails, including if the string is + null or empty. + + + + + + Parses a number whose format follows the JSON specification (RFC 8259). + Roughly speaking, a valid number consists of an optional minus sign, one + or more basic digits (starting with 1 to 9 unless the only digit is 0), an + optional decimal point (".", full stop) with one or more basic digits, and + an optional letter E or e with an optional plus or minus sign and one or + more basic digits (the exponent). + A string to parse. The string is not allowed to contain white space + characters, including spaces. + If true, no decimal points or exponents are allowed in the string. + If true, only positive numbers are allowed (the leading minus is + disallowed). + If true, returns positive zero if the number is a zero that starts with a + minus sign (such as "-0" or "-0.0"). Otherwise, returns negative zero in + this case. + A CBOR object that represents the parsed number. Returns null if the + parsing fails, including if the string is null or empty. + + + + + + Specifies options for encoding and decoding CBOR objects. + + + + + + Initializes a new instance of the class. + + + + + + Initializes a new instance of the class. + A value indicating whether to always encode strings with a + definite-length encoding. + A value indicating whether to disallow duplicate keys when reading CBOR + objects from a data stream. + + + + + + Initializes a new instance of the class.A value indicating whether to + always encode strings with a definite-length encoding.A value indicating whether to + disallow duplicate keys when reading CBOR objects from a data + stream.Either true or false + . + + + + + Gets a value indicating whether to disallow duplicate keys when reading + CBOR objects from a data stream. Used only when decoding CBOR objects. + A value indicating whether to disallow duplicate keys when reading CBOR + objects from a data stream. + + + + + + Returns an options object containing the flags shared by this and another + options object. + The parameter + + is a CBOREncodeOptions object. + A CBOREncodeOptions object. + + + + + + Gets a value indicating whether CBOR objects are written + out using the CTAP2 canonical CBOR encoding form. In this form, + CBOR tags are not used, map keys are written out in a canonical + order, and non-integer numbers and integers 2^63 or greater are + written as 64-bit binary floating-point numbers.true if CBOR objects are written out using the CTAP2 + canonical CBOR encoding form; otherwise, false.. In this + form, CBOR tags are not used, map keys are written out in a + canonical order, and non-integer numbers and integers 2^63 or + greater are written as 64-bit binary floating-point + numbers. + + + + + Default options for CBOR objects. Disallow duplicate keys, and always + encode strings using definite-length encoding. These are recommended + settings for the options that may be adopted by certain CBORObject methods + in the next major version. + + + + + + Disallow duplicate keys when reading CBOR objects from a data stream. + Used only when decoding CBOR objects. Value: 2. + + + + + + Always encode strings with a definite-length encoding. Used only when + encoding CBOR objects. Value: 1. + + + + + + No special options for encoding/decoding. Value: 0. + + + + + + Returns an options object containing the combined flags of this and + another options object. + The parameter + + is a CBOREncodeOptions object. + A new CBOREncodeOptions object. + + + + + + Gets a value indicating whether to always encode strings with a + definite-length encoding. + A value indicating whether to always encode strings with a + definite-length encoding. + + + + +Gets this options object's value. + + This options object's value. + + + + + Exception thrown for errors involving CBOR data. + + + + + + Initializes a new instance of the class. + + + + + + Initializes a new instance of the class. + The parameter + + is a text string. + + + + + + Initializes a new instance of the class. Uses the given message and inner exception. + The parameter + + is a text string. + The parameter + + is an Exception object. + + + + +Registers an object that converts objects of a given type to CBOR objects + (called a CBOR converter). + + A Type object specifying the type that the converter converts to CBOR + objects. + + The parameter + + A CBOR converter. + + + Must be the same as the "type" parameter. + + This object. + + The parameter + + or + + is null. + + "Converter doesn't contain a proper ToCBORObject method". + + + + + Represents an object in Concise Binary Object Representation (CBOR) and + contains methods for reading and writing CBOR data. CBOR is defined in RFC + 7049. + Converting CBOR objects + + There are many ways to get a CBOR object, including from bytes, + objects, streams and JSON, as described below. + + To and from byte arrays: + The CBORObject.DecodeFromBytes method converts a byte array in CBOR + format to a CBOR object. The EncodeToBytes method converts a CBOR object + to its corresponding byte array in CBOR format. + + To and from data streams: + The CBORObject.Write methods write many kinds of objects to a data + stream, including numbers, CBOR objects, strings, and arrays of numbers + and strings. The CBORObject.Read method reads a CBOR object from a data + stream. + + To and from other objects: + The CBORObject.FromObject method converts many kinds of objects to a CBOR object, including numbers, strings, and arrays and maps of numbers + and strings. Methods like AsDouble, AsByte, and AsString convert a CBOR + object to different types of object. The CBORObject.ToObject method converts a CBOR object to an object of a given type; for example, a CBOR array to a native List (or ArrayList in Java), or a CBOR integer to an int or long. + + To and from JSON: + This class also doubles as a reader and writer of JavaScript Object + Notation (JSON). The CBORObject.FromJSONString method converts JSON to a + CBOR object, and the ToJSONString method converts a CBOR object to a + JSON string. + + In addition, the CBORObject.WriteJSON method writes many kinds of + objects as JSON to a data stream, including numbers, CBOR objects, + strings, and arrays of numbers and strings. The CBORObject.Read method + reads a CBOR object from a JSON data stream. + + Comparison Considerations: + + Instances of CBORObject should not be compared for equality using the + "==" operator; it's possible to create two CBOR objects with the same + value but not the same reference. (The "==" operator might only check if + each side of the operator is the same instance.) + + This class's natural ordering (under the CompareTo method) is not + consistent with the Equals method. This means that two values that + compare as equal under the CompareTo method might not be equal under the + Equals method. This is important to consider especially if an + application wants to compare numbers, since the CBOR number type + supports numbers of different formats, such as big integers, rational + numbers, and arbitrary-precision decimal numbers. + + Another consideration is that two values that are otherwise equal may + have different tags. To strip the tags from a CBOR object before + comparing, use the + Untag + method. + + To compare two numbers, the CompareToIgnoreTags or CompareTo method + should be used. Which method to use depends on whether two equal values + should still be considered equal if they have different tags. + + Although this class is inconsistent with the Equals method, it is safe + to use CBORObject instances as hash keys as long as all of the keys are + untagged text strings (which means GetTags returns an empty array and + the Type property, or "getType()" in Java, returns TextString). This is + because the natural ordering of these instances is consistent with the + Equals method. + + Thread Safety: + + CBOR objects that are numbers, "simple values", and text strings are + immutable (their values can't be changed), so they are inherently safe + for use by multiple threads. + + CBOR objects that are arrays, maps, and byte strings are mutable, but + this class doesn't attempt to synchronize reads and writes to those + objects by multiple threads, so those objects are not thread safe + without such synchronization. + + One kind of CBOR object is called a map, or a list of key-value pairs. + Keys can be any kind of CBOR object, including numbers, strings, arrays, + and maps. However, text strings are the most suitable to use as keys; + other kinds of CBOR object are much better used as map values instead, + keeping in mind that some of them are not thread safe without + synchronizing reads and writes to them. + + To find the type of a CBOR object, call its Type property (or + "getType()" in Java). The return value can be Number, Boolean, + SimpleValue, or TextString for immutable CBOR objects, and Array, Map, + or ByteString for mutable CBOR objects. + + Nesting Depth: + + The DecodeFromBytes and Read methods can only read objects with a + limited maximum depth of arrays and maps nested within other arrays and + maps. The code sets this maximum depth to 500 (allowing more than enough + nesting for most purposes), but it's possible that stack overflows in + some runtimes might lower the effective maximum nesting depth. When the + nesting depth goes above 500, the DecodeFromBytes and Read methods throw + a CBORException. + + The ReadJSON and FromJSONString methods currently have nesting depths + of 1000. + + + + + + + Gets this object's absolute value.This object's absolute without its negative + sign.This object's + type is not a number type. + + + + + Adds a new object to the end of this array. (Used to + throw ArgumentNullException on a null reference, but now converts + the null reference to CBORObject.Null, for convenience with the + Object overload of this method). + NOTE: This method + can't be used to add a tag to an existing CBOR object. To create a + CBOR object with a given tag, call the + CBORObject.FromObjectAndTag + method and pass the CBOR object + and the desired tag number to that method. + The parameter is a CBOR + object.This instance.This object is + not an array.The following example creates a CBOR array and adds several + CBOR objects, one of which has a custom CBOR tag, to that array. + Note the chaining behavior made possible by this method. + CBORObject obj = CBORObject.NewArray() + .Add(CBORObject.False) + .Add(CBORObject.FromObject(5)) + .Add(CBORObject.FromObject("text string")) + .Add(CBORObject.FromObjectAndTag(9999, 1)); + + + + + + + Converts an object to a CBOR object and adds it to + the end of this array. + NOTE: This method can't be used + to add a tag to an existing CBOR object. To create a CBOR object + with a given tag, call the CBORObject.FromObjectAndTag + method and pass the CBOR object and the desired tag number to that + method. + The parameter is a CBOR + object.This instance.This object is + not an array.The type of is not supported.The following example creates a CBOR array and adds several + CBOR objects, one of which has a custom CBOR tag, to that array. + Note the chaining behavior made possible by this method. + CBORObject obj = CBORObject.NewArray() + .Add(CBORObject.False) + .Add(5) + .Add("text string") + .Add(CBORObject.FromObjectAndTag(9999, 1)); + + + + + + + Adds a new key and its value to this CBOR map, or adds the + value if the key doesn't exist. +NOTE: This method can't be used to add a tag to an existing CBOR object. To create a CBOR object with a given tag, call the CBORObject.FromObjectAndTag method and pass the CBOR object and the desired tag number to that method.An object representing the key, which will be + converted to a CBORObject. Can be null, in which case this value is + converted to CBORObject.Null.An object representing the value, which will + be converted to a CBORObject. Can be null, in which case this value + is converted to CBORObject.Null.This instance.The parameter + already exists in this map.This object is + not a map.The parameter + or has an + unsupported type. + + + + + Registers an object that converts objects of a given type + to CBOR objects (called a CBOR converter).A Type object specifying the type that the + converter converts to CBOR objects.The parameter + is an ICBORConverter object.Must be the same as the "type" + parameter.The parameter + or is + null."Converter doesn't contain a + proper ToCBORObject method". + + + + + Registers an object that validates CBOR objects with new + tags.An arbitrary-precision integer.The parameter is + an ICBORTag object.The parameter + or is + null.The parameter + is less than 0 or greater than + (2^64-1). + + + + + Registers an object that validates CBOR objects with new + tags.An arbitrary-precision integer.The parameter is + an ICBORTag object.The parameter + or is + null.The parameter + is less than 0 or greater than + (2^64-1). + + + + + Finds the sum of two CBOR numbers.The parameter is a + CBOR object.The parameter is a + CBOR object.A CBORObject object.Either or both + operands are not numbers (as opposed to Not-a-Number, + NaN). + + + + + Converts this object to an arbitrary-precision integer. + Fractional values are truncated to an integer.The closest big integer to this object.This object's + type is not a number type, including if this object is + CBORObject.Null.This object's value is + infinity or not-a-number (NaN). + + + + + Returns false if this object is False, Null, or Undefined; + otherwise,true. + False if this object is False, Null, or Undefined; otherwise,true. + + + + + + Converts this object to a byte (0 to 255). Floating point + values are truncated to an integer.The closest byte-sized integer to this object.This object's + type is not a number type.This object's value + exceeds the range of a byte (would be less than 0 or greater than + 255 when truncated to an integer). + + + + + Converts this object to a .NET decimal. + The closest big integer to this object. + This object's type is not a number type. + This object's value exceeds the range of a .NET decimal. + + + + + + Converts this object to a 64-bit floating point + number.The closest 64-bit floating point number to this object. + The return value can be positive infinity or negative infinity if + this value exceeds the range of a 64-bit floating point + number.This object's + type is not a number type. + + + + + Converts this object to a decimal number.A decimal number for this object's value. If this object + is a rational number with a nonterminating decimal expansion, + returns a decimal number rounded to 34 digits.This object's + type is not a number type, including if this object is + CBORObject.Null. + + + + + Converts this object to an arbitrary-precision binary + floating point number.An arbitrary-precision binary floating point number for + this object's value. Note that if this object is a decimal number + with a fractional part, the conversion may lose information + depending on the number. If this object is a rational number with a + nonterminating binary expansion, returns a binary floating-point + number rounded to 113 bits.This object's + type is not a number type, including if this object is + CBORObject.Null. + + + + + Converts this object to an arbitrary-precision integer. + Fractional values are truncated to an integer.The closest big integer to this object.This object's + type is not a number type, including if this object is + CBORObject.Null.This object's value is + infinity or not-a-number (NaN). + + + + + Converts this object to a rational number.A rational number for this object's value.This object's + type is not a number type, including if this object is + CBORObject.Null. + + + + + Converts this object to a decimal number.A decimal number for this object's value. If this object + is a rational number with a nonterminating decimal expansion, + returns a decimal number rounded to 34 digits.This object's + type is not a number type, including if this object is + CBORObject.Null. + + + + + Converts this object to an arbitrary-precision binary + floating point number.An arbitrary-precision binary floating point number for + this object's value. Note that if this object is a decimal number + with a fractional part, the conversion may lose information + depending on the number. If this object is a rational number with a + nonterminating binary expansion, returns a binary floating-point + number rounded to 113 bits.This object's + type is not a number type, including if this object is + CBORObject.Null. + + + + + Converts this object to a rational number.A rational number for this object's value.This object's + type is not a number type, including if this object is + CBORObject.Null. + + + + + Converts this object to a 16-bit signed integer. Floating + point values are truncated to an integer.The closest 16-bit signed integer to this + object.This object's + type is not a number type.This object's value + exceeds the range of a 16-bit signed integer. + + + +Converts this object to a 32-bit signed integer. Non-integer number values + are truncated to an integer. (NOTE: To determine whether this method call + can succeed, call the + CanTruncatedIntFitInInt32 + method before calling this method. Checking whether this object's type is + + CBORType.Number + is not sufficient. See the example.). + + The closest 32-bit signed integer to this object. + + This object's type is not a number type. + + This object's value exceeds the range of a 32-bit signed integer. + + The following example code (originally written in C# for the .NET + Framework) shows a way to check whether a given CBOR object stores a + 32-bit signed integer before getting its value. + + CBORObject obj = CBORObject.FromInt32(99999); if(obj.IsIntegral + && obj.CanTruncatedIntFitInInt32()) { // Not an Int32; + handle the error Console.WriteLine("Not a 32-bit integer."); } else + { Console.WriteLine("The value is " + obj.AsInt32()); } + + + + +Converts this object to a 64-bit signed integer. Non-integer numbers are + truncated to an integer. (NOTE: To determine whether this method call can + succeed, call the + CanTruncatedIntFitInInt64 + method before calling this method. Checking whether this object's type is + + CBORType.Number + is not sufficient. See the example.). + + The closest 64-bit signed integer to this object. + + This object's type is not a number type. + + This object's value exceeds the range of a 64-bit signed integer. + + The following example code (originally written in C# for the .NET + Framework) shows a way to check whether a given CBOR object stores a + 64-bit signed integer before getting its value. + + CBORObject obj = CBORObject.FromInt64(99999); if(obj.IsIntegral + && obj.CanTruncatedIntFitInInt64()) { // Not an Int64; + handle the error Console.WriteLine("Not a 64-bit integer."); } else + { Console.WriteLine("The value is " + obj.AsInt64()); } + + + + + + Converts this object to an 8-bit signed integer. + An 8-bit signed integer. + + + + + + Converts this object to a 32-bit floating point + number.The closest 32-bit floating point number to this object. + The return value can be positive infinity or negative infinity if + this object's value exceeds the range of a 32-bit floating point + number.This object's + type is not a number type. + + + + + Gets the value of this object as a text string.Gets this object's string.This object's + type is not a string, including if this object is + CBORObject.Null.The following example code (originally written in C# for the + .NET Framework) shows an idiom for returning a string value if a + CBOR object is a text string, or null + if the CBOR object is + a CBOR null. + CBORObject obj = CBORObject.FromString("test"); + string str = obj.IsNull ? null : obj.AsString(); + + + + + + + Converts this object to a 16-bit unsigned integer. The return value will + be truncated as necessary. + A 16-bit unsigned integer. + This object's value is outside the range of a 16-bit unsigned integer. + + + + + + Converts this object to a 32-bit unsigned integer. The return value will + be truncated as necessary. + A 32-bit unsigned integer. + This object's value is outside the range of a 32-bit unsigned integer. + + + + + + Converts this object to a 64-bit unsigned integer. Non-integer values are + truncated to an integer. + The closest big integer to this object. + This object's type is not a number type. + This object's value exceeds the range of a 64-bit unsigned integer. + + + + + + Returns whether this object's value can be converted to a 64-bit floating + point number without its value being rounded to another numerical value. + Whether this object's value can be converted to a 64-bit floating point + number without its value being rounded to another numerical value. Returns true if this is a + not-a-number value, even if the value's diagnostic information can' t fit + in a 64-bit floating point number. + + + + + + Returns whether this object's numerical value is an integer, is -(2^31) or + greater, and is less than 2^31. + true + if this object's numerical value is an integer, is -(2^31) or greater, and + is less than 2^31; otherwise, false + . + + + + + + Returns whether this object's numerical value is an integer, is -(2^63) or + greater, and is less than 2^63. + true + if this object's numerical value is an integer, is -(2^63) or greater, and + is less than 2^63; otherwise, false + . + + + + + + Returns whether this object's value can be converted to a 32-bit floating + point number without its value being rounded to another numerical value. + Whether this object's value can be converted to a 32-bit floating point + number without its value being rounded to another numerical value. Returns true if this is a + not-a-number value, even if the value's diagnostic information can' t fit + in a 32-bit floating point number. + + + + + + Returns whether this object's value, truncated to an integer, would be + -(2^31) or greater, and less than 2^31. + true + if this object's value, truncated to an integer, would be -(2^31) or + greater, and less than 2^31; otherwise, false + . + + + + + + Returns whether this object's value, truncated to an integer, would be + -(2^63) or greater, and less than 2^63. + true + if this object's value, truncated to an integer, would be -(2^63) or + greater, and less than 2^63; otherwise, false + . + + + + +Removes all items from this CBOR array or all keys and values from this + CBOR map. + + This object is not a CBOR array or CBOR map. + + + + + Compares two CBOR objects. + In this implementation: + + The null pointer (null reference) is considered less than any + other object. + If either object is true, false, CBORObject.Null, or the + undefined value, it is treated as less than the other value. If + both objects have one of these four values, then undefined is less + than CBORObject.Null, which is less than false, which is less than + true. + If both objects are numbers, their mathematical values are + compared. Here, NaN (not-a-number) is considered greater than any + number. + If both objects are simple values other than true, false, + CBORObject.Null, and the undefined value, the objects are compared + according to their ordinal numbers. + If both objects are arrays, each element is compared. If one + array is shorter than the other and the other array begins with + that array (for the purposes of comparison), the shorter array is + considered less than the longer array. + If both objects are strings, compares each string code-point + by code-point, as though by the DataUtilities.CodePointCompare + method. + If both objects are maps, compares each map as though each + were an array with the sorted keys of that map as the array's + elements. If both maps have the same keys, their values are + compared in the order of the sorted keys. + If each object is a different type, then they are sorted by + their type number, in the order given for the CBORType + enumeration. + If each object has different tags and both objects are + otherwise equal under this method, each element is compared as + though each were an array with that object's tags listed in order + from outermost to innermost. + This method is not consistent with the Equals + method.A value to compare with.Less than 0, if this value is less than the other object; + or 0, if both values are equal; or greater than 0, if this value is + less than the other object or if the other object is + null.An internal error occurred. + + + + + Compares this object and another CBOR object, ignoring the tags they + have, if any. See the CompareTo method for more information on the + comparison function. + A value to compare with. + Less than 0, if this value is less than the other object; or 0, if both + values are equal; or greater than 0, if this value is less than the other + object or if the other object is null. + + + + + + Determines whether a value of the given key exists in this + object.An object that serves as the key.true if the given key is found, or false if the + given key is not found or this object is not a map.Key is null (as + opposed to CBORObject.Null). + + + + + Determines whether a value of the given key exists in this + object.A string that serves as the key.true if the given key (as a CBOR object) is found, + or false if the given key is not found or this object is not a + map.Key is + null. + + + + + Gets or sets the ICBORConverter object. + The ICBORConverter object. + + + + +Gets or sets the converter's ToCBORObject method. + + The converter's ToCBORObject method. + + + + + Gets the number of keys in this map, or the number of items in this + array, or 0 if this item is neither an array nor a map. + The number of keys in this map, or the number of items in this array, or + 0 if this item is neither an array nor a map. + + + + + + At the moment, use the overload of this method that takes + a + object. The object + CBOREncodeOptions.Default contains recommended settings for + CBOREncodeOptions, and those settings may be adopted by this + overload (without a CBOREncodeOptions argument) in the next major + version. + Generates a CBOR object from an array of CBOR-encoded + bytes.A byte array in which a single CBOR object is encoded.A CBOR object decoded from the given byte array.There was an error in + reading or parsing the data. This includes cases where not all of + the byte array represents a CBOR object. This exception is also + thrown if the parameter is + empty.The parameter + is null. + + + +Generates a CBOR object from an array of CBOR-encoded bytes, using the + given + CBOREncodeOptions + object to control the decoding process. + + A byte array in which a single CBOR object is encoded. + + The parameter + + is a CBOREncodeOptions object. + + A CBOR object decoded from the given byte array. + + There was an error in reading or parsing the data. This includes cases + where not all of the byte array represents a CBOR object. This exception + is also thrown if the parameter + + is empty. + + The parameter + + is null. + + The following example (originally written in C# for the .NET version) + implements a method that decodes a text string from a CBOR byte array. + It's successful only if the CBOR object contains an untagged text + string. + + private static String DecodeTextString(byte[] bytes){ + if(bytes == null){ throw new + ArgumentNullException(nameof(mapObj));} if(bytes.Length + == 0 || bytes[0]<0x60 || bytes[0]>0x7f){throw new + CBORException();} return + CBORObject.DecodeFromBytes(bytes, + CBOREncodeOptions.Default).AsString(); } + + + + + + Divides a CBORObject object by the value of a CBORObject object. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The quotient of the two objects. + + + + + + At the moment, use the overload of this method that takes a + + object. The object + CBOREncodeOptions.Default + contains recommended settings for CBOREncodeOptions, and those + settings may be adopted by this overload (without a CBOREncodeOptions + argument) in the next major version. + + Writes the binary representation of this CBOR object and returns a byte + array of that representation. If the CBOR object contains CBOR maps, or + is a CBOR map itself, the keys to the map are written out to the byte + array in an undefined order. The example code given in + + can be used to write out certain keys of a CBOR map in a given order. + + A byte array in CBOR format. + + + + + + Writes the binary representation of this CBOR object and + returns a byte array of that representation, using the specified + options for encoding the object to CBOR format. If the CBOR object + contains CBOR maps, or is a CBOR map itself, the keys to the map + are written out to the byte array in an undefined order. The + example code given in + + can be used to write out certain keys of a CBOR map in a given + order.Options for encoding the data to + CBOR.A byte array in CBOR format.The parameter + is null. + + + + + Compares the equality of two CBOR objects. Not-a-number values can be + considered equal by this method. + The object to compare. + true + if the objects are equal; otherwise, false + . + + + + + + Determines whether this object and another object are equal and have the + same type. Not-a-number values can be considered equal by this method. + The parameter + + is an arbitrary object. + true + if the objects are equal; otherwise, false + . + + + + + + Represents the value false. + + + + + + At the moment, use the overload of this method that takes + a + object. The object + CBOREncodeOptions.Default contains recommended settings for + CBOREncodeOptions, and those settings may be adopted by this + overload (without a CBOREncodeOptions argument) in the next major + version. + Generates a CBOR object from a text string in JavaScript + Object Notation (JSON) format. + If a JSON object has the same key, only the last given value + will be used for each duplicated key.A string in JSON format. The entire string must + contain a single JSON object and not multiple objects. The string + may not begin with a byte-order mark (U+FEFF).A CBORObject object.The parameter + is null.The string is not in + JSON format. + + + + + Generates a CBOR object from a text string in JavaScript + Object Notation (JSON) format, using the specified options to + control the decoding process. + By default, if a JSON object has the same key, only the last + given value will be used for each duplicated key.A string in JSON format. The entire string must + contain a single JSON object and not multiple objects. The string + may not begin with a byte-order mark (U+FEFF).The parameter is a + CBOREncodeOptions object.A CBORObject object.The parameter + is null.The string is not in + JSON format. + + + + + Generates a CBOR object from an arbitrary-precision integer. + An arbitrary-precision value. + A CBOR number. + + + + + + Generates a CBOR object from a CBOR object. + The parameter + + is a CBOR object. + Same as. + + + + + + Generates a CBOR object from an array of CBOR objects. + An array of CBOR objects. + A CBOR object where each element of the given array is copied to a new + array, or CBORObject.Null if the value is null. + + + + + + Generates a CBOR object from a decimal number. + An arbitrary-precision decimal number. + A CBOR number. + + + + + + Generates a CBOR object from an arbitrary-precision binary floating-point + number. + An arbitrary-precision binary floating-point number. + A CBOR number. + + + + + + Generates a CBOR object from an arbitrary-precision binary floating-point + number. + An arbitrary-precision binary floating-point number. + A CBOR number. + + + + + + Generates a CBOR object from a decimal number. + An arbitrary-precision decimal number. + A CBOR number. + + + + + + Generates a CBOR object from an arbitrary-precision binary floating-point + number. + An arbitrary-precision binary floating-point number. + A CBOR number. + + + + + + Generates a CBOR object from an arbitrary-precision integer. + An arbitrary-precision value. + A CBOR number. + + + + + + Generates a CBOR object from a rational number. + A rational number. + A CBOR number. + + + + + + Returns the CBOR true value or false value, depending on "value". + Either True or False. + CBORObject.True if value is true; otherwise CBORObject.False. + + + + + + Generates a CBOR object from a byte (0 to 255). + The parameter + + is a byte (from 0 to 255). + A CBORObject object. + + + + + + Generates a CBOR object from a byte array. The byte + array is copied to a new byte array. (This method can't be used to + decode CBOR data from a byte array; for that, use the + DecodeFromBytes method instead.). + A byte array. Can be null.A CBOR byte string object where each byte of the given + byte array is copied to a new array, or CBORObject.Null if the + value is null.The following example encodes a text string to a UTF-8 byte + array, then uses the array to create a CBOR byte string object. It + is not recommended to use Encoding.UTF8.GetBytes + in .NET, or + the getBytes() + method in Java to do this. For instance, + Encoding.UTF8 + begins the encoded string with a byte-order + mark, and getBytes() + encodes text strings in an unspecified + character encoding. Both behaviors can be undesirable. Instead, use + the DataUtilities.GetUtf8Bytes + method to convert text + strings to UTF-8. + /* true does character replacement of + invalid UTF-8; false throws an exception + on invalid UTF-8 */ + byte[] bytes = DataUtilities.GetUtf8Bytes( + textString, true); + CBORObject cbor = CBORObject.FromBytes(bytes); + + + + + + + Generates a CBOR string object from a Unicode + character.The parameter is a + char object.A CBORObject object.The parameter + is a surrogate code point.Note that this method's behavior may change in the future. + Currently, it converts ' char's to text strings, but it may change + to convert them to integers instead. + + + + + Converts a .NET decimal to a CBOR object. + The parameter + + is a Decimal object. + A CBORObject object with the same value as the .NET decimal. + + + + + + Generates a CBOR object from a 64-bit floating-point number. + The parameter + + is a 64-bit floating-point number. + A CBORObject object. + + + + + + Generates a CBOR object from a 16-bit signed integer. + The parameter + + is a 16-bit signed integer. + A CBORObject object. + + + + + + Generates a CBOR object from a 32-bit signed integer. + The parameter + + is a 32-bit signed integer. + A CBORObject object. + + + + + + Generates a CBOR object from an array of 32-bit integers. + An array of 32-bit integers. + A CBOR array object where each element of the given array is copied to a + new array, or CBORObject.Null if the value is null. + + + + + + Generates a CBOR object from a 64-bit signed integer. + The parameter + + is a 64-bit signed integer. + A CBORObject object. + + + + + + Generates a CBOR object from an array of 64-bit integers. + An array of 64-bit integers. + A CBOR array object where each element of the given array is copied to a + new array, or CBORObject.Null if the value is null. + + + + + + Generates a CBORObject from an arbitrary object. See the overload of this + method that takes a PODOptions argument. + The parameter + + is an arbitrary object. + A CBOR object corresponding to the given object. Returns CBORObject.Null + if the object is null. + + + + + + Generates a CBORObject from an arbitrary object, using the + given options to control how certain objects are converted to CBOR + objects. The following types are specially handled by this method: + null; primitive types; string; CBORObject; the EDecimal, + EFloat, EInteger, and ERational classes in + the new + PeterO.Numbers + library (in .NET) or the + com.github.peteroupc/numbers + artifact (in Java); the legacy ExtendedDecimal, + ExtendedFloat, BigInteger, and + ExtendedRational classes in this library; arrays; + enumerations ( Enum objects); and maps. (See also the other + overloads to the FromObject method.) + In the .NET version, if the object is a type not specially + handled by this method, returns a CBOR map with the values of each + of its read/write properties (or all properties in the case of a + compiler-generated type). Properties are converted to their + camel-case names (meaning if a name starts with A to Z, that letter + is lower-cased). If the property name begins with the word "Is" + followed by an upper-case A to Z, the "Is" prefix is deleted from + the name. (Passing the appropriate "options" parameter can be done + to control whether the "Is" prefix is removed and whether a + camel-case conversion happens.) Also, .NET Enum objects will + be converted to their integer values, and a multidimensional array + is converted to an array of arrays. + In the Java version, if the object is a type not specially + handled by this method, this method checks the CBOR object for + methods starting with the word "get" or "is" (either word followed + by an upper-case A to Z) that take no parameters, and returns a + CBOR map with one entry for each such method found. For each method + found, the starting word "get" or "is" is deleted from its name, + and the name is converted to camel case (meaning if a name starts + with A to Z, that letter is lower-cased). (Passing the appropriate + "options" parameter can be done to control whether the "is" prefix + is removed and whether a camel-case conversion happens.) Also, Java + Enum objects will be converted to the result of their + name method. + If the input is a byte array, the byte array is copied to a + new byte array. (This method can't be used to decode CBOR data from + a byte array; for that, use the DecodeFromBytes method + instead.). + +If the input is a text string, a CBOR text string object will be created. To create a CBOR byte string object from a text string, see the example given in +. +REMARK: The behavior of this method is likely to change in the next major version (4.0). There are certain inconsistencies between the ToObject method and the FromObject method as well as between the .NET and Java versions of FromObject. For one thing, DateTime/Date objects are converted differently between the two versions -- either as CBOR maps with their "get" properties (Java) or as tag-0 strings (.NET) -- this difference has to remain for backward compatibility with version 3.0. For another thing, the treatment of properties/getters starting with "Is" is subtly inconsistent between the .NET and Java versions of FromObject, especially when using certain PODOptions. A certain consistency between .NET and Java and between FromObject and ToObject are sought for version 4.0. It is also hoped that-- +the ToObject method will support deserializing to objects consisting of fields and not getters ("getX()" methods), both in .NET and in Java, and +both FromObject and ToObject will be better designed, in version 4.0, so that backward-compatible improvements are easier to make. +The parameter is an + arbitrary object.An object containing options to control how + certain objects are converted to CBOR objects.A CBOR object corresponding to the given object. Returns + CBORObject.Null if the object is null.The parameter is null. + + + + + Converts a signed 8-bit integer to a CBOR object. + The parameter + + is an 8-bit signed integer. + A CBORObject object. + + + + + + Generates a CBOR object from a 32-bit floating-point number. + The parameter + + is a 32-bit floating-point number. + A CBORObject object. + + + + + + Generates a CBOR object from a text string.A string value. Can be null.A CBOR object representing the string, or CBORObject.Null + if stringValue is null.The string contains an + unpaired surrogate code point. + + + + + Converts a 16-bit unsigned integer to a CBOR object. + A 16-bit unsigned integer. + A CBORObject object. + + + + + + Converts a 32-bit unsigned integer to a CBOR object. + A 32-bit unsigned integer. + A CBORObject object. + + + + + + Converts a 64-bit unsigned integer to a CBOR object. + A 64-bit unsigned integer. + A CBORObject object. + + + + + + Generates a CBOR object from an arbitrary object and gives + the resulting object a tag.An arbitrary object. If the tag number is 2 + or 3, this must be a byte string whose bytes represent an integer + in little-endian byte order, and the value of the number is 1 minus + the integer's value for tag 3. If the tag number is 4 or 5, this + must be an array with two elements: the first must be an integer + representing the exponent, and the second must be an integer + representing a mantissa.Tag number. The tag number 55799 can be + used to mark a "self-described CBOR" object. This document does not attempt to list all CBOR tags and their meanings. An up-to-date list can be found at the CBOR Tags registry maintained by the Internet Assigned Numbers Authority (iana.org/assignments/cbor-tags).A CBOR object where the object + is converted to a CBOR object and given the tag .The parameter + is less than 0 or greater than 2^64-1, + or 's type is unsupported.The parameter + is null. + + + + + Generates a CBOR object from an arbitrary object and gives + the resulting object a tag.An arbitrary object. If the tag number is 2 + or 3, this must be a byte string whose bytes represent an integer + in little-endian byte order, and the value of the number is 1 minus + the integer's value for tag 3. If the tag number is 4 or 5, this + must be an array with two elements: the first must be an integer + representing the exponent, and the second must be an integer + representing a mantissa.Tag number. The tag number 55799 can be + used to mark a "self-described CBOR" object. This document does not attempt to list all CBOR tags and their meanings. An up-to-date list can be found at the CBOR Tags registry maintained by the Internet Assigned Numbers Authority (iana.org/assignments/cbor-tags).A CBOR object where the object + is converted to a CBOR object and given the tag .The parameter + is less than 0 or greater than 2^64-1, + or 's type is unsupported.The parameter + is null. + + + + + Generates a CBOR object from an arbitrary object and gives + the resulting object a tag.An arbitrary object. If the tag number + is 2 or 3, this must be a byte string whose bytes represent an + integer in little-endian byte order, and the value of the number is + 1 minus the integer's value for tag 3. If the tag number is 4 or 5, + this must be an array with two elements: the first must be an + integer representing the exponent, and the second must be an + integer representing a mantissa.A 32-bit integer that specifies a tag + number. The tag number 55799 can be used to mark a "self-described + CBOR" object. This document does not attempt to list all CBOR tags and their meanings. An up-to-date list can be found at the CBOR Tags registry maintained by the Internet Assigned Numbers Authority (iana.org/assignments/cbor-tags).A CBOR object where the object is converted to a CBOR object and given the + tag .The parameter + is less than 0 or 's type is unsupported. + + + + + Generates a CBOR object from an arbitrary object and gives the resulting + object a tag. + The parameter + + is an arbitrary object. + A 64-bit integer that specifies a tag + number. The tag number 55799 can be used to mark a "self-described + CBOR" object. This document does not attempt to list all CBOR tags and their meanings. An up-to-date list can be found at the CBOR Tags registry maintained by the Internet Assigned Numbers Authority (iana.org/assignments/cbor-tags). + A CBOR object where the object + + is converted to a CBOR object and given the tag + + . + + + + + + Generates a CBOR object from an enumerable set of objects. + An object that implements the IEnumerable interface. In the .NET version, + this can be the return value of an iterator or the result of a LINQ query. + + + A type convertible to CBORObject. + A CBOR object where each element of the given enumerable object is + converted to a CBOR object and copied to a new array, or CBORObject.Null + if the value is null. + + + + + + Generates a CBOR object from a list of objects. + An array of CBOR objects. Can be null. + + A type convertible to CBORObject. + A CBOR object where each element of the given array is converted to a + CBOR object and copied to a new array, or CBORObject.Null if the value is + null. + + + + + + Generates a CBOR object from a map of objects. + A map of CBOR objects. + + A type convertible to CBORObject; the type of the keys. + + A type convertible to CBORObject; the type of the values. + A CBOR object where each key and value of the given map is converted to a + CBOR object and copied to a new map, or CBORObject.Null if + + is null. + + + + + + Creates a CBOR object from a simple value + number.The parameter is a 32-bit signed integer.A CBORObject object.The parameter + is less than 0, greater than 255, or + from 24 through 31. + + + + + Gets a list of all tags, from outermost to innermost. + An array of tags, or the empty string if this object is untagged. + + + + + + Gets the byte array used in this object, if this object is + a byte string, without copying the data to a new one. This method's + return value can be used to modify the array's contents. Note, + though, that the array' s length can't be changed.The byte array held by this CBOR object.This object is + not a byte string. + + + + + Calculates the hash code of this object. No application or process IDs + are used in the hash code calculation. + A 32-bit hash code. + + + + + + Gets a list of all tags, from outermost to innermost. + An array of tags, or the empty string if this object is untagged. + + + + + + Returns whether this object has a tag of the given + number.The tag value to search for.true if this object has a tag of the given number; otherwise, false.BigTagValue is + null.BigTagValue is less + than 0. + + + + + Returns whether this object has a tag of the given + number.The tag value to search for.true if this object has a tag of the given number; otherwise, false.BigTagValue is + null.BigTagValue is less + than 0. + + + + + Returns whether this object has a tag of the given + number.The tag value to search for.true if this object has a tag of the given number; otherwise, false.TagValue is less than + 0.The parameter + "obj" is null. + + + + + Gets the last defined tag for this CBOR data item, or -1 if the item is + untagged. + The last defined tag for this CBOR data item, or -1 if the item is + untagged. + + + + + + Inserts an object at the specified position in this CBOR + array.Zero-based index to insert at.An object representing the value, which will + be converted to a CBORObject. Can be null, in which case this value + is converted to CBORObject.Null.This instance.This object is + not an array.The parameter + has an unsupported type; or is not a valid index into this array. + + + + + Gets a value indicating whether this value is a CBOR false value. + true + If this value is a CBOR false value; otherwise, . + false + . + + + + + + Gets a value indicating whether this CBOR object + represents a finite number.true If this CBOR object represents a finite number; + otherwise, . false. + + + + + Gets a value indicating whether this CBOR object represents infinity. + true + if this CBOR object represents infinity; otherwise, false + . + + + + + + Gets a value indicating whether this object represents an + integral number, that is, a number without a fractional part. + Infinity and not-a-number are not considered integral.true If this object represents an integral number, + that is, a number without a fractional part; otherwise, . + false. + + + + + Gets a value indicating whether this CBOR object represents a + not-a-number value (as opposed to whether this object's type is not a + number type). + true + if this CBOR object represents a not-a-number value (as opposed to + whether this object's type is not a number type); otherwise, false + . + + + + + + Gets a value indicating whether this object is a negative number. + true + If this object is a negative number; otherwise, . + false + . + + + + + + Gets a value indicating whether this CBOR object represents negative + infinity. + true + if this CBOR object represents negative infinity; otherwise, false + . + + + + + + Gets a value indicating whether this value is a CBOR null + value.true If this value is a CBOR null value; otherwise, . + false. + + + + + Gets a value indicating whether this CBOR object represents positive + infinity. + true + if this CBOR object represents positive infinity; otherwise, false + . + + + + + + Gets a value indicating whether this data item has at + least one tag.true If this data item has at least one tag; + otherwise, . false. + + + + + Gets a value indicating whether this value is a CBOR true + value.true If this value is a CBOR true value; otherwise, . + false. + + + + + Gets a value indicating whether this value is a CBOR + undefined value.true If this value is a CBOR undefined value; + otherwise, . false. + + + +Gets a value indicating whether this object's value equals 0. + + true + If this object's value equals 0; otherwise, . + false + . + + + + + Gets the value of a CBOR object in this map, using a CBOR + object as the key.The parameter is a CBOR + object.A CBORObject object.The key is null + (as opposed to CBORObject.Null); or the set method is called and + the value is null.This object is + not a map. + + + + + Gets the value of a CBOR object by integer index in this + array.Zero-based index of the element.A CBORObject object.This object is + not an array.The parameter + "value" is null (as opposed to CBORObject.Null). + + + + + Gets the value of a CBOR object in this map, using a + string as the key.A key that points to the desired value.A CBORObject object.The key is + null.This object is + not a map. + + + + + Gets a collection of the keys of this CBOR object in an + undefined order.A collection of the keys of this CBOR object.This object is + not a map. + + + + + Gets the last defined tag for this CBOR data item, or -1 if the item is + untagged. + The last defined tag for this CBOR data item, or -1 if the item is + untagged. + + + + + + Gets the outermost tag for this CBOR data item, or -1 if the item is + untagged. + The outermost tag for this CBOR data item, or -1 if the item is untagged. + + + + + + + Multiplies two CBOR numbers.The parameter is a + CBOR object.The parameter is a + CBOR object.The product of the two numbers.Either or both + operands are not numbers (as opposed to Not-a-Number, + NaN). + + + + + A not-a-number value. + + + + + + Gets this object's value with the sign reversed.The reversed-sign form of this number.This object's + type is not a number type. + + + + + The value negative infinity. + + + + + + Creates a new empty CBOR array. + A new CBOR array. + + + + + + Creates a new empty CBOR map. + A new CBOR map. + + + + + + Represents the value null. + + + + + + Gets the outermost tag for this CBOR data item, or -1 if the item is + untagged. + The outermost tag for this CBOR data item, or -1 if the item is untagged. + + + + + + + The value positive infinity. + + + + + + At the moment, use the overload of this method that takes + a + object. The object + CBOREncodeOptions.Default contains recommended settings for + CBOREncodeOptions, and those settings may be adopted by this + overload (without a CBOREncodeOptions argument) in the next major + version. + Reads an object in CBOR format from a data stream. This + method will read from the stream until the end of the CBOR object + is reached or an error occurs, whichever happens + first.A readable data stream.A CBOR object that was read.The parameter + is null.There was an error in + reading or parsing the data. + + + + + Reads an object in CBOR format from a data stream, using + the specified options to control the decoding process. This method + will read from the stream until the end of the CBOR object is + reached or an error occurs, whichever happens first.A readable data stream.The parameter is a + CBOREncodeOptions object.A CBOR object that was read.The parameter + is null.There was an error in + reading or parsing the data. + + + + + Generates a CBOR object from a data stream in JavaScript + Object Notation (JSON) format. The JSON stream may begin with a + byte-order mark (U+FEFF). Since version 2.0, the JSON stream can + be in UTF-8, UTF-16, or UTF-32 encoding; the encoding is detected + by assuming that the first character read must be a byte-order mark + or a nonzero basic character (U+0001 to U+007F). (In previous + versions, only UTF-8 was allowed.) + If a JSON object has the same key, only the last given value + will be used for each duplicated key.A readable data stream. The sequence of bytes + read from the data stream must contain a single JSON object and not + multiple objects.A CBORObject object.The parameter + is null.An I/O error + occurred.The data stream + contains invalid encoding or is not in JSON format. + + + + + Generates a CBOR object from a data stream in JavaScript + Object Notation (JSON) format, using the specified options to + control the decoding process. The JSON stream may begin with a + byte-order mark (U+FEFF). Since version 2.0, the JSON stream can + be in UTF-8, UTF-16, or UTF-32 encoding; the encoding is detected + by assuming that the first character read must be a byte-order mark + or a nonzero basic character (U+0001 to U+007F). (In previous + versions, only UTF-8 was allowed.) + By default, if a JSON object has the same key, only the last + given value will be used for each duplicated key.A readable data stream. The sequence of bytes + read from the data stream must contain a single JSON object and not + multiple objects.The parameter is a + CBOREncodeOptions object.A CBORObject object.The parameter + is null.An I/O error + occurred.The data stream + contains invalid encoding or is not in JSON format. + + + + + Finds the remainder that results when a CBORObject object is divided by + the value of a CBORObject object. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The remainder of the two numbers. + + + + + + If this object is an array, removes the first instance of + the specified item from the array. If this object is a map, removes + the item with the given key from the map.The item or key to remove.true if the item was removed; otherwise, false.The parameter + is null (as opposed to + CBORObject.Null).The object is + not an array or map. + + + + + If this object is an array, removes the first instance of + the specified item (once converted to a CBOR object) from the array. If this object is a map, removes + the item with the given key (once converted to a CBOR object) from the map.The item or key (once converted to a CBOR object) to remove.true if the item was removed; otherwise, false.The parameter + is null (as opposed to + CBORObject.Null).The object is + not an array or map. + + + +Removes the item at the given index of this CBOR array. + + The index, starting at 0, of the item to remove. + + Returns "true" if the object was removed. Returns "false" if the given + index is less than 0, or is at least as high as the number of items in the + array. + + This object is not a CBOR array. + + + + + Maps an object to a key in this CBOR map, or adds the + value if the key doesn't exist.An object representing the key, which will be + converted to a CBORObject. Can be null, in which case this value is + converted to CBORObject.Null.An object representing the value, which will + be converted to a CBORObject. Can be null, in which case this value + is converted to CBORObject.Null.This instance.This object is + not a map.The parameter + or has an + unsupported type. + + + +Gets this value's sign: -1 if negative; 1 if positive; 0 if zero. + + This value's sign: -1 if negative; 1 if positive; 0 if zero. + + This object's type is not a number type, including the special + not-a-number value (NaN). + + + + + Gets the simple value ID of this object, or -1 if this object is not a + simple value (including if the value is a floating-point number). + The simple value ID of this object, or -1 if this object is not a simple + value (including if the value is a floating-point number). + + + + + + Finds the difference between two CBOR number + objects.The parameter is a + CBOR object.The parameter is a + CBOR object.The difference of the two objects.Either or both + operands are not numbers (as opposed to Not-a-Number, + NaN). + + + + + Converts this object to a string in JavaScript Object + Notation (JSON) format, using the specified options to control the + encoding process. See the overload to JSONString taking a + JSONOptions argument. + If the CBOR object contains CBOR maps, or is a CBOR map + itself, the keys to the map are written out to the JSON string in + an undefined order. The example code given in + + can be used to write out certain keys of a CBOR map in a given + order to a JSON string.A text string. + + + +Converts this object to a string in JavaScript Object Notation (JSON) + format, using the specified options to control the encoding process. This + function works not only with arrays and maps, but also integers, strings, + byte arrays, and other JSON data types. Notes: + + If this object contains maps with non-string keys, the keys are + converted to JSON strings before writing the map as a JSON string. + + If the CBOR object contains CBOR maps, or is a CBOR map itself, the + keys to the map are written out to the JSON string in an undefined + order. + + If a number in the form of an arbitrary-precision binary float has a + very high binary exponent, it will be converted to a double before + being converted to a JSON string. (The resulting double could overflow + to infinity, in which case the arbitrary-precision binary float is + converted to null.) + + The string will not begin with a byte-order mark (U+FEFF); RFC 8259 + (the JSON specification) forbids placing a byte-order mark at the + beginning of a JSON string. + + Byte strings are converted to Base64 URL without whitespace or padding + by default (see section 4.1 of RFC 7049). A byte string will instead + be converted to traditional base64 without whitespace or padding by + default if it has tag 22, or base16 for tag 23. Padding will be + included in the Base64 URL or traditional base64 form if + Base64Padding + in the JSON options is set to + true + . (To create a CBOR object with a given tag, call the + CBORObject.FromObjectAndTag + method and pass the CBOR object and the desired tag number to that + method.) + + Rational numbers will be converted to their exact form, if possible, + otherwise to a high-precision approximation. (The resulting + approximation could overflow to infinity, in which case the rational + number is converted to null.) + + Simple values other than true and false will be converted to null. + (This doesn't include floating-point numbers.) + + Infinity and not-a-number will be converted to null. + + + The example code given below (originally written in C# for the .NET + version) can be used to write out certain keys of a CBOR map in a given + order to a JSON string. + + /* Generates a JSON string of 'mapObj' whose keys are in the order given + in 'keys' . Only keys found in 'keys' will be written if they exist in + 'mapObj'. */ private static string KeysToJSONMap(CBORObject mapObj, + IList<CBORObject> keys){ if (mapObj == null) { throw new + ArgumentNullException)nameof(mapObj));} if (keys == null) { throw + new ArgumentNullException)nameof(keys));} if (obj.Type != + CBORType.Map) { throw new ArgumentException("'obj' is not a map."); } + StringBuilder builder = new StringBuilder(); var first = true; + builder.Append("{"); for (CBORObject key in keys) { if + (mapObj.ContainsKey(key)) { if (!first) {builder.Append(", ");} var + keyString=(key.CBORType == CBORType.String) ? key.AsString() : + key.ToJSONString(); builder.Append(CBORObject.FromObject(keyString) + .ToJSONString()) .Append(":").Append(mapObj[key].ToJSONString()); + first=false; } } return builder.Append("}").ToString(); } + + + An object containing the options to control writing the CBOR object to + JSON. + + A text string containing the converted object. + + The parameter + + is null. + + + +Converts this CBOR object to an object of an arbitrary type. See the + documentation for the overload of this method taking a CBORTypeMapper + parameter for more information. This method (without a CBORTypeMapper + parameter) allows all data types not otherwise handled to be eligible for + Plain-Old-Data serialization. + + The type, class, or interface that this method's return value will belong + to. To express a generic type in Java, see the example. + Note: + For security reasons, an application should not base this parameter on + user input or other externally supplied data. Whenever possible, this + parameter should be either a type specially handled by this method (such + as + int + or + String + ) or a plain-old-data type (POCO or POJO type) within the control of the + application. If the plain-old-data type references other data types, those + types should likewise meet either criterion above. + + The converted object. + + The given type + + , or this object's CBOR type, is not supported. + + The parameter + + is null. + + The given object's nesting is too deep, or another error occurred when + serializing the object. + + Java offers no easy way to express a generic type, at least none as easy + as C#'s + typeof + operator. The following example, written in Java, is a way to specify + that the return value will be an ArrayList of String objects. + + Type arrayListString = new ParameterizedType(){ public Type[] + getActualTypeArguments(){ /* Contains one type parameter, String */ + return new Type[]{ String.class }; } public Type getRawType(){ /* Raw + type is ArrayList */ return ArrayList.class; } public Type + getOwnerType(){ return null; } }; ArrayList<String> array = + (ArrayList<String>) cborArray.ToObject(arrayListString); + + By comparison, the C# version is much shorter. + + var array = (List<String>)cborArray.ToObject( + typeof(List<String>)); + + + + + + Converts this CBOR object to an object of an arbitrary + type. See the documentation for the overload of this method taking + a CBORTypeMapper and PODOptions parameters parameters for more information.The type, class, or interface that this method's + return value will belong to. To express a generic type in Java, see + the example. Note: + For security reasons, an application + should not base this parameter on user input or other externally + supplied data. Whenever possible, this parameter should be either a + type specially handled by this method (such as int + or + String + ///) or a plain-old-data type (POCO or POJO type) + within the control of the application. If the plain-old-data type + references other data types, those types should likewise meet + either criterion above.This parameter controls which data types are + eligible for Plain-Old-Data deserialization and includes custom + converters from CBOR objects to certain data types.The converted object.The given type + , or this object's CBOR type, is not + supported.The parameter + is null.The given object's nesting + is too deep, or another error occurred when serializing the + object. + + + +Converts this CBOR object to an object of an arbitrary type. The + following cases are checked in the logical order given (rather than the + strict order in which they are implemented by this library): + + + If the type is + CBORObject + , return this object. + + If the given object is + CBORObject.Null + (with or without tags), returns + null + . + + If the object is of a type corresponding to a type converter mentioned + in the + + parameter, that converter will be used to convert the CBOR object to + an object of the given type. Type converters can be used to override + the default conversion behavior of almost any object. + + If the type is + object + , return this object. + + If the type is + char + , converts single-character CBOR text strings and CBOR integers from + 0 through 65535 to a + char + object and returns that + char + object. + + If the type is + bool + ( + boolean + in Java), returns the result of AsBoolean. + + If the type is a primitive integer type ( + byte + , + int + , + short + , + long + , as well as + sbyte + , + ushort + , + uint + , and + ulong + in .NET) or a primitive floating-point type ( + float + , + double + , as well as + decimal + in .NET), returns the result of the corresponding As* method. + + If the type is + String + , returns the result of AsString. + + If the type is + EDecimal + , + EFloat + , + EInteger + , or + ERational + in the + + PeterO.Numbers + + library (in .NET) or the + + com.github.peteroupc/numbers + + artifact (in Java), returns the result of the corresponding As* + method. + + If the type is an enumeration ( + Enum + ///) type this CBOR object is a text string or an integer, returns + the appropriate enumerated constant. (For example, if + MyEnum + includes an entry for + MyValue + , this method will return + MyEnum.MyValue + if the CBOR object represents + "MyValue" + or the underlying value for + MyEnum.MyValue + .) + Note: + If an integer is converted to a .NET Enum constant, and that integer + is shared by more than one constant of the same type, it is undefined + which constant from among them is returned. (For example, if + MyEnum.Zero = 0 + and + MyEnum.Null = 0 + , converting 0 to + MyEnum + may return either + MyEnum.Zero + or + MyEnum.Null + .) As a result, .NET Enum types with constants that share an + underlying value should not be passed to this method. + + If the type is + byte[] + (a one-dimensional byte array) and this CBOR object is a byte string, + returns a byte array which this CBOR byte string's data will be copied + to. (This method can't be used to encode CBOR data to a byte array; + for that, use the EncodeToBytes method instead.) + + If the type is a one-dimensional or multidimensional array type and + this CBOR object is an array, returns an array containing the items in + this CBOR object. + + If the type is List or the generic or non-generic IList, ICollection, + or IEnumerable, (or ArrayList, List, Collection, or Iterable in Java), + and if this CBOR object is an array, returns an object conforming to + the type, class, or interface passed to this method, where the object + will contain all items in this CBOR array. + + If the type is Dictionary or the generic or non-generic IDictionary + (or HashMap or Map in Java), and if this CBOR object is a map, returns + an object conforming to the type, class, or interface passed to this + method, where the object will contain all keys and values in this CBOR + map. + + If the type is an enumeration constant ("enum"), and this CBOR object + is an integer or text string, returns the enumeration constant with + the given number or name, respectively. (Enumeration constants made up + of multiple enumeration constants, as allowed by .NET, can only be + matched by number this way.) + + If the type is + DateTime + (or + Date + in Java) , returns a date/time object if the CBOR object's outermost + tag is 0 or 1. + + If the type is + Uri + (or + URI + in Java), returns a URI object if possible. + + If the type is + Guid + (or + UUID + in Java), returns a UUID object if possible. + + Plain-Old-Data deserialization: If the object is a type not specially + handled above, the type includes a zero-argument constructor (default + or not), this CBOR object is a CBOR map, and the "mapper" parameter + allows this type to be eligible for Plain-Old-Data deserialization, + then this method checks the given type for eligible setters as + follows: + + (*) In the .NET version, eligible setters are the public, nonstatic + setters of properties with a public, nonstatic getter. If a class has + two properties of the form "X" and "IsX", where "X" is any name, or + has multiple properties with the same name, those properties are + ignored. + + (*) In the Java version, eligible setters are public, nonstatic + methods starting with "set" followed by a character other than a basic + digit or lower-case letter, that is, other than "a" to "z" or "0" to + "9", that take one parameter. The class containing an eligible setter + must have a public, nonstatic method with the same name, but starting + with "get" or "is" rather than "set", that takes no parameters and + does not return void. (For example, if a class has "public + setValue(String)" and "public getValue()", "setValue" is an eligible + setter. However, "setValue()" and "setValue(String, int)" are not + eligible setters.) If a class has two otherwise eligible setters with + the same name, but different parameter type, they are not eligible + setters. + + Then, the method creates an object of the given type and invokes each + eligible setter with the corresponding value in the CBOR map, if any. + Key names in the map are matched to eligible setters according to the + rules described in the + + documentation. Note that for security reasons, certain types are not + supported even if they contain eligible setters. + + + REMARK: A certain consistency between .NET and Java and between + FromObject and ToObject are sought for version 4.0. It is also hoped + that-- + + + the ToObject method will support deserializing to objects consisting + of fields and not getters ("getX()" methods), both in .NET and in + Java, and + + both FromObject and ToObject will be better designed, in version 4.0, + so that backward-compatible improvements are easier to make. + + + + The type, class, or interface that this method's return value will belong + to. To express a generic type in Java, see the example. + Note: + For security reasons, an application should not base this parameter on + user input or other externally supplied data. Whenever possible, this + parameter should be either a type specially handled by this method (such + as + int + or + String + ///) or a plain-old-data type (POCO or POJO type) within the control of + the application. If the plain-old-data type references other data types, + those types should likewise meet either criterion above. + + This parameter controls which data types are eligible for Plain-Old-Data + deserialization and includes custom converters from CBOR objects to + certain data types. + + Specifies options for controlling deserialization of CBOR objects. + + The converted object. + + The given type + + , or this object's CBOR type, is not supported. + + The parameter + + is null. + + The given object's nesting is too deep, or another error occurred when + serializing the object. + + Java offers no easy way to express a generic type, at least none as easy + as C#'s + typeof + operator. The following example, written in Java, is a way to specify + that the return value will be an ArrayList of String objects. + + Type arrayListString = new ParameterizedType() { public Type[] + getActualTypeArguments() { // Contains one type parameter, String return + new Type[] { String.class }; } public Type getRawType() { /* Raw type is + ArrayList */ return ArrayList.class; } public Type getOwnerType() { + return null; } }; ArrayList<String> array = + (ArrayList<String>) cborArray.ToObject(arrayListString); + + By comparison, the C# version is much shorter. + + var array = (List<String>)cborArray.ToObject( + typeof(List<String>)); + + + + + + Converts this CBOR object to an object of an arbitrary + type. See the documentation for the overload of this method taking + a CBORTypeMapper and PODOptions parameters for more information. This method + (without a CBORTypeMapper parameter) allows all data types not + otherwise handled to be eligible for Plain-Old-Data + serialization.The type, class, or interface that this method's + return value will belong to. To express a generic type in Java, see + the example. Note: + For security reasons, an application + should not base this parameter on user input or other externally + supplied data. Whenever possible, this parameter should be either a + type specially handled by this method (such as int + or + String + ///) or a plain-old-data type (POCO or POJO type) + within the control of the application. If the plain-old-data type + references other data types, those types should likewise meet + either criterion above.Specifies options for controlling deserialization of CBOR objects. + The converted object.The given type + , or this object's CBOR type, is not + supported.The parameter + is null.The given object's nesting + is too deep, or another error occurred when serializing the + object. + + + + + Converts this CBOR object to an object of an arbitrary type. See for further information. +The type, class, or interface that this method's return value will belong + to. Note: For security reasons, + an application should not base this parameter on user input or other externally supplied + data. Whenever possible, this parameter should be either a type specially handled by + this method (such as int or String) or a plain-old-data type + (POCO type) within the control of the application. If the plain-old-data + type references other data types, those types should likewise meet either criterion above.The converted object.The given type "T", or this object's CBOR type, is not supported. + + + + + + Converts this CBOR object to an object of an arbitrary type. + See + for + further information.This parameter controls which data types are eligible for Plain-Old-Data + deserialization and includes custom converters from CBOR objects to + certain data types. + The type, class, or interface that this + method's return value will belong to. Note: For security + reasons, an application should not base this parameter on user + input or other externally supplied data. Whenever possible, this + parameter should be either a type specially handled by this method + (such as int or String ) or a plain-old-data type + (POCO type) within the control of the application. If the + plain-old-data type references other data types, those types should + likewise meet either criterion above.The converted object.The given type + "T", or this object's CBOR type, is not supported. + + + + + Converts this CBOR object to an object of an arbitrary type. + See + for + further information.This parameter controls which data types are eligible for Plain-Old-Data + deserialization and includes custom converters from CBOR objects to + certain data types. + Specifies options for controlling deserialization of CBOR objects. + The type, class, or interface that this + method's return value will belong to. Note: For security + reasons, an application should not base this parameter on user + input or other externally supplied data. Whenever possible, this + parameter should be either a type specially handled by this method + (such as int or String ) or a plain-old-data type + (POCO type) within the control of the application. If the + plain-old-data type references other data types, those types should + likewise meet either criterion above.The converted object.The given type + "T", or this object's CBOR type, is not supported. + + + + + Converts this CBOR object to an object of an arbitrary type. + See + for + further information.Specifies options for controlling deserialization of CBOR objects. + The type, class, or interface that this + method's return value will belong to. Note: For security + reasons, an application should not base this parameter on user + input or other externally supplied data. Whenever possible, this + parameter should be either a type specially handled by this method + (such as int or String ) or a plain-old-data type + (POCO type) within the control of the application. If the + plain-old-data type references other data types, those types should + likewise meet either criterion above.The converted object.The given type + "T", or this object's CBOR type, is not supported. + + + + + Returns this CBOR object in string form. The format is intended to be + human-readable, not machine-readable, the format is not intended to be + parsed, and the format may change at any time. The returned string +is not necessarily in JavaScript Object Notation (JSON); to convert +CBOR objects to JSON strings, use the method instead. + A text representation of this object. + + + + + + Represents the value true. + + + + + + Gets the general data type of this CBOR object. + The general data type of this CBOR object. + + + + + + Represents the value undefined. + + + + + + Gets an object with the same value as this one but without the tags it + has, if any. If this object is an array, map, or byte string, the data + will not be copied to the returned object, so changes to the returned + object will be reflected in this one. + A CBORObject object. + + + + + + Gets an object with the same value as this one but without this object's + outermost tag, if any. If this object is an array, map, or byte string, + the data will not be copied to the returned object, so changes to the + returned object will be reflected in this one. + A CBORObject object. + + + + + + Gets a collection of the values of this CBOR object, if + it's a map or an array. If this object is a map, returns one value + for each key in the map in an undefined order. If this is an array, + returns all the values of the array in the order they are listed. + (This method can't be used to get the bytes in a CBOR byte string; + for that, use the GetByteArray method instead.).A collection of the values of this CBOR map or + array.This object is + not a map or an array. + + + + + Writes a big integer in CBOR format to a data + stream.Big integer to write. Can be null.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a CBOR object to a CBOR data stream.The value to write. Can be null.A writable data stream.The parameter + is null. + + + + + Writes a decimal floating-point number in CBOR format to a + data stream, as follows: + + If the value is null, writes the byte 0xF6. + If the value is negative zero, infinity, or NaN, converts the + number to a double and writes that double. If + negative zero should not be written this way, use the Plus method + to convert the value beforehand. + If the value has an exponent of zero, writes the value as an + unsigned integer or signed integer if the number can fit either + type or as a big integer otherwise. + In all other cases, writes the value as a decimal + number.The arbitrary-precision decimal number to + write. Can be null.Stream to write to.The parameter + is null.An I/O error + occurred. + + + + + Writes a binary floating-point number in CBOR format to a + data stream as follows: + + If the value is null, writes the byte 0xF6. + If the value is negative zero, infinity, or NaN, converts the + number to a double and writes that double. If + negative zero should not be written this way, use the Plus method + to convert the value beforehand. + If the value has an exponent of zero, writes the value as an + unsigned integer or signed integer if the number can fit either + type or as a big integer otherwise. + In all other cases, writes the value as a big + float.An arbitrary-precision binary float.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a rational number in CBOR format to a data + stream.An arbitrary-precision rational + number.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a decimal floating-point number in CBOR format to a + data stream, as follows: + + If the value is null, writes the byte 0xF6. + If the value is negative zero, infinity, or NaN, converts the + number to a double and writes that double. If + negative zero should not be written this way, use the Plus method + to convert the value beforehand. + If the value has an exponent of zero, writes the value as an + unsigned integer or signed integer if the number can fit either + type or as a big integer otherwise. + In all other cases, writes the value as a decimal + number.The arbitrary-precision decimal number to + write. Can be null.Stream to write to.The parameter + is null.An I/O error + occurred. + + + + + Writes a binary floating-point number in CBOR format to a + data stream as follows: + + If the value is null, writes the byte 0xF6. + If the value is negative zero, infinity, or NaN, converts the + number to a double and writes that double. If + negative zero should not be written this way, use the Plus method + to convert the value beforehand. + If the value has an exponent of zero, writes the value as an + unsigned integer or signed integer if the number can fit either + type or as a big integer otherwise. + In all other cases, writes the value as a big + float.An arbitrary-precision binary float.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a big integer in CBOR format to a data + stream.Big integer to write. Can be null.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a rational number in CBOR format to a data + stream.An arbitrary-precision rational + number.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a Boolean value in CBOR format to a data + stream.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a byte (0 to 255) in CBOR format to a data stream. + If the value is less than 24, writes that byte. If the value is 25 + to 255, writes the byte 24, then this byte's value.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a Unicode character as a string in CBOR format to a + data stream.The value to write.A writable data stream.The parameter + is null.The parameter + is a surrogate code point.An I/O error + occurred.Note that this method's behavior may change in the future. + Currently, it converts ' char's to text strings, but it may change + to convert them to integers instead. + + + + + Writes a 64-bit floating-point number in CBOR format to a + data stream.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a 16-bit signed integer in CBOR format to a data + stream.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a 32-bit signed integer in CBOR format to a data + stream.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a 64-bit signed integer in CBOR format to a data + stream.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + At the moment, use the overload of this method that takes a + + object. The object + CBOREncodeOptions.Default + contains recommended settings for CBOREncodeOptions, and those + settings may be adopted by this overload (without a CBOREncodeOptions + argument) in the next major version. + + Writes a CBOR object to a CBOR data stream. See the three-parameter + Write method that takes a CBOREncodeOptions. + + The parameter + + is an arbitrary object. + A writable data stream. + + + + + + Writes an arbitrary object to a CBOR data stream, using + the specified options for controlling how the object is encoded to + CBOR data format. If the object is convertible to a CBOR map or a + CBOR object that contains CBOR maps, the keys to those maps are + written out to the data stream in an undefined order. The example + code given in + + can be used to write out certain keys of a CBOR map in a given + order. Currently, the following objects are supported: + + Lists of CBORObject. + Maps of CBORObject. The keys to the map are written out to + the data stream in an undefined order. + Null. + Byte arrays, which will always be written as definite-length + byte strings. + String objects, which will be written as indefinite-length + text strings if their size exceeds a certain threshold (this + behavior may change in future versions of this library). + Any object accepted by the FromObject static + methods.The arbitrary object to be serialized. Can + be null.A writable data stream.CBOR options for encoding the CBOR object to + bytes.The object's type is + not supported.The parameter + or is + null. + + + + + Writes an 8-bit signed integer in CBOR format to a data stream. + The parameter + + is an 8-bit signed integer. + A writable data stream. + + + + + + Writes a 32-bit floating-point number in CBOR format to a + data stream.The value to write.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + At the moment, use the overload of this method that takes + a + object. The object + CBOREncodeOptions.Default contains recommended settings for + CBOREncodeOptions, and those settings may be adopted by this + overload (without a CBOREncodeOptions argument) in the next major + version. + Writes a string in CBOR format to a data stream. The string + will be encoded using indefinite-length encoding if its length + exceeds a certain threshold (this behavior may change in future + versions of this library).The string to write. Can be null.A writable data stream.The parameter + is null.An I/O error + occurred. + + + + + Writes a string in CBOR format to a data stream, using the + given options to control the encoding process.The string to write. Can be null.A writable data stream.Options for encoding the data to + CBOR.The parameter + is null.An I/O error + occurred. + + + + + Writes a 16-bit unsigned integer in CBOR format to a data stream. + A 16-bit unsigned integer. + A writable data stream. + + + + + + Writes a 32-bit unsigned integer in CBOR format to a data stream. + A 32-bit unsigned integer. + A writable data stream. + + + + + + Writes a 64-bit unsigned integer in CBOR format to a data stream. + A 64-bit unsigned integer. + A writable data stream. + The parameter + + is null. + + + + + + Converts an arbitrary object to a string in JavaScript Object Notation + (JSON) format, as in the ToJSONString method, and writes that string to a + data stream in UTF-8. If the object is convertible to a CBOR map, or to a + CBOR object that contains CBOR maps, the keys to those maps are written + out to the JSON string in an undefined order. The example code given in + + can be used to write out certain keys of a CBOR map in a given order to a + JSON string. + The parameter + + is an arbitrary object. + A writable data stream. + + + + + + Converts this object to a string in JavaScript Object + Notation (JSON) format, as in the ToJSONString method, and writes + that string to a data stream in UTF-8. If the CBOR object contains + CBOR maps, or is a CBOR map, the keys to the map are written out to + the JSON string in an undefined order. The example code given in + + can be used to write out certain keys of a CBOR map in a given + order to a JSON string.A writable data stream.An I/O error + occurred.The parameter + is null. + + + + + Converts this object to a string in JavaScript Object + Notation (JSON) format, as in the ToJSONString method, and writes + that string to a data stream in UTF-8, using the given JSON options + to control the encoding process. If the CBOR object contains CBOR + maps, or is a CBOR map, the keys to the map are written out to the + JSON string in an undefined order. The example code given in + + can be used to write out certain keys of a CBOR map in a given + order to a JSON string.A writable data stream.An object containing the options to control + writing the CBOR object to JSON.An I/O error + occurred.The parameter + is null. + + + +At the moment, use the overload of this method that takes a + + object. The object + CBOREncodeOptions.Default + contains recommended settings for CBOREncodeOptions, and those + settings may be adopted by this overload (without a CBOREncodeOptions + argument) in the next major version. + + Writes this CBOR object to a data stream. If the CBOR object contains + CBOR maps, or is a CBOR map, the keys to the map are written out to the + data stream in an undefined order. See the examples (written in C# for + the .NET version) for ways to write out certain keys of a CBOR map in a + given order. + + + A writable data stream. + + The parameter + + is null. + + An I/O error occurred. + + The following example shows a method that writes each key of 'mapObj' to + 'outputStream', in the order given in 'keys', where 'mapObj' is written + out in the form of a CBOR + definite-length map + . Only keys found in 'keys' will be written if they exist in 'mapObj'. + + private static void WriteKeysToMap(CBORObject mapObj, + IList<CBORObject> keys, Stream outputStream){ if(mapObj + == null){ throw new + ArgumentNullException(nameof(mapObj));} if(keys == + null){throw new ArgumentNullException(nameof(keys));} + if(outputStream == null){throw new + ArgumentNullException(nameof(outputStream));} + if(obj.Type!=CBORType.Map){ throw new ArgumentException("'obj' + is not a map."); } int keyCount = 0; for (CBORObject key in keys) + { if(mapObj.ContainsKey(key)){ keyCount++; } } + CBORObject.WriteValue(outputStream, 5, keyCount); for (CBORObject key in + keys) { if(mapObj.ContainsKey(key)){ + key.WriteTo(outputStream); mapObj[key].WriteTo(outputStream); } + } } + + The following example shows a method that writes each key of 'mapObj' to + 'outputStream', in the order given in 'keys', where 'mapObj' is written + out in the form of a CBOR + indefinite-length map + . Only keys found in 'keys' will be written if they exist in 'mapObj'. + + private static void WriteKeysToIndefMap(CBORObject mapObj, + IList<CBORObject> keys, Stream outputStream){ if(mapObj + == null){ throw new + ArgumentNullException(nameof(mapObj));} if(keys == + null){throw new ArgumentNullException(nameof(keys));} + if(outputStream == null){throw new + ArgumentNullException(nameof(outputStream));} + if(obj.Type!=CBORType.Map){ throw new ArgumentException("'obj' + is not a map."); } outputStream.WriteByte((byte)0xBF); for + (CBORObject key in keys) { if(mapObj.ContainsKey(key)){ + key.WriteTo(outputStream); mapObj[key].WriteTo(outputStream); } + } outputStream.WriteByte((byte)0xff); } + + The following example shows a method that writes out a list of objects + to 'outputStream' as an + indefinite-length CBOR array + . + + private static void WriteToIndefArray( IList<object> list, + Stream outputStream){ if(list == null){ throw new + ArgumentNullException(nameof(list));} if(outputStream == + null){throw new + ArgumentNullException(nameof(outputStream));} + outputStream.WriteByte((byte)0x9f); for (object item in list) { new + CBORObject(item).WriteTo(outputStream); } + outputStream.WriteByte((byte)0xff); } + + + + + + Writes this CBOR object to a data stream, using the + specified options for encoding the data to CBOR format. If the CBOR + object contains CBOR maps, or is a CBOR map, the keys to the map + are written out to the data stream in an undefined order. The + example code given in + + can be used to write out certain keys of a CBOR map in a given + order.A writable data stream.Options for encoding the data to + CBOR.The parameter + is null.An I/O error + occurred."Unexpected data + type". + + + + + Writes a CBOR major type number and an integer 0 or + greater associated with it to a data stream, where that integer is + passed to this method as an arbitrary-precision integer. This is a + low-level method that is useful for implementing custom CBOR + encoding methodologies. This method encodes the given major type + and value in the shortest form allowed for the major + type.A writable data stream.The CBOR major type to write. This is a + number from 0 through 7 as follows. 0: integer 0 or greater; 1: + negative integer; 2: byte string; 3: UTF-8 text string; 4: array; + 5: map; 6: tag; 7: simple value. See RFC 7049 for details on these + major types.An integer 0 or greater associated with + the major type, as follows. 0: integer 0 or greater; 1: the + negative integer's absolute value is 1 plus this number; 2: length + in bytes of the byte string; 3: length in bytes of the UTF-8 text + string; 4: number of items in the array; 5: number of key-value + pairs in the map; 6: tag number; 7: simple value number, which must + be in the interval [0, 23] or [32, 255]. For major types 0 to 6, + this number may not be greater than 2^64 - 1.The number of bytes ordered to be written to the data + stream.The parameter + is 7 and value is greater than + 255.The parameter or is + null. + + + + + Writes a CBOR major type number and an integer 0 or + greater associated with it to a data stream, where that integer is + passed to this method as a 32-bit signed integer. This is a + low-level method that is useful for implementing custom CBOR + encoding methodologies. This method encodes the given major type + and value in the shortest form allowed for the major + type.A writable data stream.The CBOR major type to write. This is a + number from 0 through 7 as follows. 0: integer 0 or greater; 1: + negative integer; 2: byte string; 3: UTF-8 text string; 4: array; + 5: map; 6: tag; 7: simple value. See RFC 7049 for details on these + major types.An integer 0 or greater associated with the + major type, as follows. 0: integer 0 or greater; 1: the negative + integer's absolute value is 1 plus this number; 2: length in bytes + of the byte string; 3: length in bytes of the UTF-8 text string; 4: + number of items in the array; 5: number of key-value pairs in the + map; 6: tag number; 7: simple value number, which must be in the + interval [0, 23] or [32, 255].The number of bytes ordered to be written to the data + stream.Value is from 24 to 31 + and major type is 7.The parameter is null.In the following example, an array of three objects is + written as CBOR to a data stream. + CBORObject.WriteValue(stream, 4, 3); /* array, length 3 */ + CBORObject.Write("hello world", stream); /* item 1 */ + CBORObject.Write(25, stream); /* item 2 */ + CBORObject.Write(false, stream); // item 3 + + In the following example, a map consisting of two key-value + pairs is written as CBOR to a data stream. + CBORObject.WriteValue(stream, 5, 2); // map, 2 pairs + CBORObject.Write("number", stream); // key 1 + CBORObject.Write(25, stream); // value 1 + CBORObject.Write("string", stream); // key 2 + CBORObject.Write("hello", stream); // value 2 + +In the following example (originally written in C# for the .NET Framework version), a text string is written as CBOR to a data stream. +string str = "hello world"; +byte[] bytes = DataUtilities.GetUtf8Bytes(str, true); +CBORObject.WriteValue(stream, 4, bytes.Length); +stream.Write(bytes, 0, bytes.Length); + + + + + + + Writes a CBOR major type number and an integer 0 or + greater associated with it to a data stream, where that integer is + passed to this method as a 64-bit signed integer. This is a + low-level method that is useful for implementing custom CBOR + encoding methodologies. This method encodes the given major type + and value in the shortest form allowed for the major + type.A writable data stream.The CBOR major type to write. This is a + number from 0 through 7 as follows. 0: integer 0 or greater; 1: + negative integer; 2: byte string; 3: UTF-8 text string; 4: array; + 5: map; 6: tag; 7: simple value. See RFC 7049 for details on these + major types.An integer 0 or greater associated with the + major type, as follows. 0: integer 0 or greater; 1: the negative + integer's absolute value is 1 plus this number; 2: length in bytes + of the byte string; 3: length in bytes of the UTF-8 text string; 4: + number of items in the array; 5: number of key-value pairs in the + map; 6: tag number; 7: simple value number, which must be in the + interval [0, 23] or [32, 255].The number of bytes ordered to be written to the data + stream.Value is from 24 to 31 + and major type is 7.The parameter is null. + + + + + Writes a CBOR major type number and an integer 0 or + greater associated with it to a data stream, where that integer is + passed to this method as a 32-bit unsigned integer. This is a + low-level method that is useful for implementing custom CBOR + encoding methodologies. This method encodes the given major type + and value in the shortest form allowed for the major + type.A writable data stream.The CBOR major type to write. This is a + number from 0 through 7 as follows. 0: integer 0 or greater; 1: + negative integer; 2: byte string; 3: UTF-8 text string; 4: array; + 5: map; 6: tag; 7: simple value. See RFC 7049 for details on these + major types.An integer 0 or greater associated with the + major type, as follows. 0: integer 0 or greater; 1: the negative + integer's absolute value is 1 plus this number; 2: length in bytes + of the byte string; 3: length in bytes of the UTF-8 text string; 4: + number of items in the array; 5: number of key-value pairs in the + map; 6: tag number; 7: simple value number, which must be in the + interval [0, 23] or [32, 255].The number of bytes ordered to be written to the data + stream.The parameter is null. + + + + + Writes a CBOR major type number and an integer 0 or + greater associated with it to a data stream, where that integer is + passed to this method as a 64-bit unsigned integer. This is a + low-level method that is useful for implementing custom CBOR + encoding methodologies. This method encodes the given major type + and value in the shortest form allowed for the major + type.A writable data stream.The CBOR major type to write. This is a + number from 0 through 7 as follows. 0: integer 0 or greater; 1: + negative integer; 2: byte string; 3: UTF-8 text string; 4: array; + 5: map; 6: tag; 7: simple value. See RFC 7049 for details on these + major types.An integer 0 or greater associated with the + major type, as follows. 0: integer 0 or greater; 1: the negative + integer's absolute value is 1 plus this number; 2: length in bytes + of the byte string; 3: length in bytes of the UTF-8 text string; 4: + number of items in the array; 5: number of key-value pairs in the + map; 6: tag number; 7: simple value number, which must be in the + interval [0, 23] or [32, 255].The number of bytes ordered to be written to the data + stream.The parameter + is 7 and value is greater than + 255.The parameter is null. + + + + + Gets a CBOR object for the number zero. + + + + + + Adds two CBOR objects and returns their result. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The sum of the two objects. + + + + + + Divides a CBORObject object by the value of a CBORObject object. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The quotient of the two objects. + + + + + + Finds the remainder that results when a CBORObject object is divided by + the value of a CBORObject object. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The remainder of the two numbers. + + + + + + Multiplies a CBORObject object by the value of a CBORObject object. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The product of the two numbers. + + + + + + Subtracts a CBORObject object from a CBORObject object. + The parameter + + is a CBOR object. + The parameter + + is a CBOR object. + The difference of the two objects. + + + + + + Implements arithmetic operations with CBOR objects. + + + + + + Implements CBOR tag 3. + + + + + + Converts a UUID to a CBOR object. + A UUID. + A CBORObject object. + + + + + + A generic CBOR tag class for strings. + + + + + + Represents a type that a CBOR object can have. + + + + + + An array of CBOR objects. + + + + + + The simple values true and false. + + + + + + An array of bytes. + + + + + + A map of CBOR objects. + + + + + + A number of any kind, including integers, big integers, floating point + numbers, and decimal numbers. The floating-point value Not-a-Number is + also included in the Number type. + + + + + + A "simple value" other than floating point values, true, and false. + + + + + + A text string. + + + + + + Specifies what kinds of CBOR objects a tag can be. This class is used + when a CBOR object is being read from a data stream. This class can't be + inherited; this is a change in version 2.0 from previous versions, where + the class was inadvertently left inheritable. + + + + + + A filter that allows any CBOR object. + + + + + + Determines whether this type filter allows CBOR arrays and the given + array index is allowed under this type filter. + An array index, starting from 0. + true + if this type filter allows CBOR arrays and the given array index is + allowed under this type filter; otherwise, false + . + + + + + + Returns whether an array's length is allowed under a filter. + An arbitrary-precision integer. + true + if this filter allows CBOR arrays and an array's length is allowed under + a filter; otherwise, false + . + The parameter + + is null. + + + + + + Returns whether an array's length is allowed under this filter. + The length of a CBOR array. + true + if this filter allows CBOR arrays and an array's length is allowed under + this filter; otherwise, false + . + + + + + + Returns whether an array's length is allowed under a filter. + The length of a CBOR array. + true + if this filter allows CBOR arrays and an array's length is allowed under + a filter; otherwise, false + . + + + + + + A filter that allows byte strings. + + + + + + Gets the type filter for this array filter by its index. + A zero-based index of the filter to retrieve. + Returns None if the index is out of range, or Any if this filter doesn't + filter an array. Returns the appropriate filter for the array index + otherwise. + + + + + + Gets the type filter for this array filter by its index. + A zero-based index of the filter to retrieve. + Returns None if the index is out of range, or Any if this filter doesn't + filter an array. Returns the appropriate filter for the array index + otherwise. + + + + + + Returns whether the given CBOR major type matches a major type allowed by + this filter. + A CBOR major type from 0 to 7. + true + if the given CBOR major type matches a major type allowed by this filter; otherwise, false + . + + + + + + A filter that allows negative integers. + + + + + + Returns whether this filter allows simple values that are not + floating-point numbers. + true + if this filter allows simple values that are not floating-point numbers; otherwise, false + . + + + + + + A filter that allows no CBOR types. + + + + + + Gets a value indicating whether CBOR objects can have the given tag + number. + A tag number. Returns false if this is less than 0. + true + if CBOR objects can have the given tag number; otherwise, false + . + The parameter + + is null. + + + + + + Gets a value indicating whether CBOR objects can have the given tag + number. + A tag number. Returns false if this is less than 0. + true + if CBOR objects can have the given tag number; otherwise, false + . + + + + + + Gets a value indicating whether CBOR objects can have the given tag + number. + A tag number. Returns false if this is less than 0. + true + if CBOR objects can have the given tag number; otherwise, false + . + + + + + + A filter that allows text strings. + + + + + + A filter that allows unsigned integers. + + + + + + Copies this filter and includes arrays of any length in the new filter. + A CBORTypeFilter object. + + + + + + Copies this filter and includes CBOR arrays with an exact length to the + new filter. + The desired maximum length of an array. + An array containing the allowed types for each element in the array. + There must be at least as many elements here as given in the arrayLength + parameter. + A CBORTypeFilter object. + The parameter arrayLength is less than 0. + The parameter elements is null. + The parameter elements has fewer elements than specified in arrayLength. + + + + + + Copies this filter and includes CBOR arrays with at least a given length + to the new filter. + The desired minimum length of an array. + An array containing the allowed types for each element in the array. + There must be at least as many elements here as given in the arrayLength + parameter. + A CBORTypeFilter object. + The parameter arrayLength is less than 0. + The parameter elements is null. + The parameter elements has fewer elements than specified in arrayLength. + + + + + + Copies this filter and includes byte strings in the new filter. + A CBORTypeFilter object. + + + + + + Copies this filter and includes floating-point numbers in the new filter. + + A CBORTypeFilter object. + + + + + + Copies this filter and includes maps in the new filter. + A CBORTypeFilter object. + + + + + + Copies this filter and includes negative integers in the new filter. + A CBORTypeFilter object. + + + + + + Copies this filter and includes a set of valid CBOR tags in the new + filter. + An array of the CBOR tags to add to the new filter. + A CBORTypeFilter object. + + + + + + Copies this filter and includes text strings in the new filter. + A CBORTypeFilter object. + + + + + + Copies this filter and includes unsigned integers in the new filter. + A CBORTypeFilter object. + + + + + + Holds converters to customize the serialization and + deserialization behavior of CBORObject.FromObject and + CBORObject#ToObject, as well as type filters for + ToObject + + + + + Registers an object that converts objects of a given type + to CBOR objects (called a CBOR converter).A Type object specifying the type that the + converter converts to CBOR objects.The parameter + is an ICBORConverter object. +This object. + +Must be the same as the "type" + parameter.The parameter + or is + null."Converter doesn't contain a + proper ToCBORObject method". + + + + Adds the fully qualified name of a Java or .NET type for use in type matching.The fully qualified name of a Java or .NET class (e.g., java.math.BigInteger or System.Globalization.CultureInfo).This object.The parameter is null.The parameter is empty. + + + + + Adds a prefix of a Java or .NET type for use in type matching. A type matches a prefix if its fully qualified name is or begins with that prefix, using codepoint-by-codepoint (case-sensitive) matching.The prefix of a Java or .NET type (e.g., `java.math.` or `System.Globalization`).This object.The parameter is null.The parameter is empty. + + + +Gets an internal API value. + An internal API value. + + + + Returns whether the given Java or .NET type name fits the filters given in this mapper.The fully qualified name of a Java or .NET class (e.g., java.math.BigInteger or System.Globalization.CultureInfo).Either true if the given Java or .NET type name fits the filters given in this mapper, or false otherwise. + + + + + Contains utility methods that may have use outside of the CBORObject + class. + + + + + + Sets this object's value to the current value times another integer. + The integer to multiply by. + This instance. + + + + + + Gets the sign of this object's value. + 1 if positive, -1 if negative, 0 if zero. + + + + + + Sets this object's value to the current value minus the given FastInteger + value. + The subtrahend. + This instance. + + + + + + Sets this object's value to the current value minus the given integer. + The subtrahend. + This instance. + + + + + + Interface implemented by classes that convert objects of arbitrary types + to CBOR objects. + + Type to convert to a CBOR object. + + + + + + Converts an object to a CBOR object. + An object to convert to a CBOR object. + A CBOR object. + + + + +Interface implemented by classes that convert objects of arbitrary types + to and from CBOR objects. + + + Type of objects that a class implementing this method can convert to and + from CBOR objects. + + + +Converts a CBOR object to an object of a type supported by the + implementing class. + + A CBOR object to convert. + + The converted object. + + An error occurred in the conversion; for example, the conversion doesn't + support the given CBOR object. + + + + + Implemented by classes that validate CBOR objects belonging to a specific + tag. + + + + + + Gets a type filter specifying what kinds of CBOR objects are supported by + this tag. + A CBOR type filter. + + + + + + Generates a CBOR object based on the data of another object. If the data + is not valid, should throw a CBORException. + A CBOR object with the corresponding tag handled by the ICBORTag object. + A CBORObject object. Note that this method may choose to return the same + object as the parameter. + + + + + + An interface for reading Unicode characters from a data source. + + + + + + Reads a sequence of Unicode code points from a data source. + Output buffer. + Index in the output buffer to start writing to. + Maximum number of code points to write. + Either a Unicode code point (from 0-0xd7ff or from 0xe000 to 0x10ffff), + or the value -1 indicating the end of the source. + + + + + + Reads a Unicode character from a data source. + Either a Unicode code point (from 0-0xd7ff or from 0xe000 to 0x10ffff), + or the value -1 indicating the end of the source. + + + + + + Includes options to control how CBOR objects are converted + to JSON. + + + + + Initializes a new instance of the class with default options. + + + + + + Initializes a new instance of the class with the given values for the options. + Whether padding is included when writing data in base64url or traditional + base64 format to JSON. + + + + + + Gets a value indicating whether padding is written out when writing base64url or traditional base64 to JSON.The default is false, no padding.The padding character is '='. + + + + + The default options for converting CBOR objects to JSON. + + + + + + Options for converting "plain old data" objects to CBOR objects. + + + + + + Initializes a new instance of the class. + + + + + + Initializes a new instance of the class. + If set to + true + remove is prefix. NOTE: May be ignored in future versions of this library. + If set to + true + use camel case. + + + + + + The default settings for "plain old data" options. + + + + + + Gets a value indicating whether the "Is" prefix in + property names is removed before they are used as keys.true If the prefix is removed; otherwise, . + false. + + + + + Gets a value indicating whether property names are + converted to camel case before they are used as keys.true If the names are converted to camel case; + otherwise, . false. + + + + + Implements CBOR string references, described at + http://cbor.schmorp.de/stringref + + + + + + Contains utility methods for processing Uniform Resource Identifiers + (URIs) and Internationalized Resource Identifiers (IRIs) under RFC3986 and + RFC3987, respectively. In the following documentation, URIs and IRIs + include URI references and IRI references, for convenience. +There are five components to a URI: scheme, authority, path, query, and fragment identifier. +The generic syntax to these components is defined in RFC3986 and extended in RFC3987. According to RFC3986, different URI schemes can further restrict the syntax of the authority, path, and query component (see also RFC 7320). However, the syntax of fragment identifiers depends on the media type (also known as MIME type) of the resource a URI references (see also RFC 3986 and RFC 7320). As of Aug. 24, 2018, only the following media types specify a syntax for fragment identifiers: + +The following application/* media types: epub+zip, pdf, senml+cbor, senml+json, senml-exi, sensml+cbor, sensml+json, sensml-exi, smil, +vnd.3gpp-v2x-local-service-information, vnd.3gpp.mcdata-signalling, +vnd.collection.doc+json, vnd.hc+json, vnd.hyper+json, +vnd.hyper-item+json, vnd.mason+json, +vnd.microsoft.portable-executable, vnd.oma.bcast.sgdu, +vnd.shootproof+json +The following image/* media types: avci, avcs, heic, heic-sequence, heif, heif-sequence +The XML media types: application/xml, application/xml-external-parsed-entity, text/xml, text/xml-external-parsed-entity, application/xml-dtd +All media types with subtypes ending in "+xml" (see RFC 7303) use XPointer Framework syntax as fragment identifiers, except the following application/* media types: dicom+xml, senml+xml, sensml+xml, ttml+xml, xliff+xml, yang-data+xml +font/collection +multipart/x-mixed-replace +text/plain +text/csv +text/html +text/markdown +text/vnd.a + + + + +Encodes characters other than "unreserved" characters for URIs. + + A string to encode. + + The encoded string. + + The parameter + + is null. + + + + + Specifies whether certain characters are allowed when parsing IRIs and + URIs. + + + + + + The rules only check for the appropriate delimiters when splitting the + path, without checking if all the characters in each component are valid. + Even with this mode, strings with unpaired surrogate code points are + considered invalid. + + + + + + The rules follow the syntax for parsing IRIs. In particular, many code + points outside the Basic Latin range (U+0000 to U+007F) are allowed. + Strings with unpaired surrogate code points are considered invalid. + + + + + + The rules only check for the appropriate delimiters when splitting the + path, without checking if all the characters in each component are valid. + Unpaired surrogate code points are treated as though they were replacement + characters instead for the purposes of these rules, so that strings with + those code points are not considered invalid strings. + + + + + + The rules only check for the appropriate delimiters when splitting the + path, without checking if all the characters in each component are valid. + Code points outside the Basic Latin range (U+0000 to U+007F) are not + allowed. + + + + + + The rules follow the syntax for parsing IRIs, except that code points + outside the Basic Latin range (U+0000 to U+007F) are not allowed. + + + + +Decodes percent-encoding (of the form "%XX" where X is a hexadecimal + digit) in the given string. Successive percent-encoded bytes are assumed + to form characters in UTF-8. + + A string that may contain percent encoding. May be null. + + The string in which percent-encoding was decoded. + + + +Decodes percent-encoding (of the form "%XX" where X is a hexadecimal + digit) in the given portion of a string. Successive percent-encoded bytes + are assumed to form characters in UTF-8. + + A string a portion of which may contain percent encoding. May be null. + + Zero-based index showing where the desired portion of + + begins. + + Zero-based index showing where the desired portion of + + ends. The character before this index is the last character. + + The portion of the given string in which percent-encoding was decoded. + Returns null if + + is ull. + + + + + Escapes characters that can't appear in URIs or IRIs. The function is + idempotent; that is, calling the function again on the result with the + same mode doesn't change the result. + A string to escape. + The parameter + + is a 32-bit signed integer. + A string possibly containing escaped characters, or null if s is null. + + + + + + Determines whether the string is a valid IRI with a scheme component. + This can be used to check for relative IRI references. + The following cases return true: + + xx-x:mm example:/ww + + The following cases return false: + x@y:/z /x/y/z example.xyz + + A string representing an IRI to check. + true + if the string is a valid IRI with a scheme component; otherwise, false + . + + + + + + Determines whether the string is a valid URI with a scheme component. + This can be used to check for relative URI references. The following cases + return true: + http://example/z xx-x:mm example:/ww + + The following cases return false: + x@y:/z /x/y/z example.xyz + + A string representing an IRI to check. + true + if the string is a valid URI with a scheme component; otherwise, false + . + + + + + + Determines whether the substring is a valid CURIE + reference under RDFA 1.1. (The CURIE reference is the part after + the colon.).A string containing a CURIE reference. Can be + null.A zero-based index showing where the desired + portion of "s" begins.The number of elements in the desired portion + of "s" (but not more than "s" 's length).true if the substring is a valid CURIE reference + under RDFA 1; otherwise, false. Returns false if is null.Either or is less than 0 or + greater than 's length, or ' s length minus is less than + .The parameter is null. + + + + + Resolves a URI or IRI relative to another URI or IRI. + A string representing a URI or IRI reference. Example: + dir/file.txt + . + A string representing an absolute URI reference. Example: + http://example.com/my/path/ + . + The resolved IRI, or null if + + is null or is not a valid IRI. If + + is null or is not a valid IRI, returns refValue. Example: + http://example.com/my/path/dir/file.txt + . + + + + + + Resolves a URI or IRI relative to another URI or IRI. + A string representing a URI or IRI reference. Example: + dir/file.txt + . Can be null. + A string representing an absolute URI reference. Example: + http://example.com/my/path/ + . + Parse mode that specifies whether certain characters are allowed when + parsing IRIs and URIs. + The resolved IRI, or null if + + is null or is not a valid IRI. If + + is null or is not a valid IRI, returns refValue. + + + + + + Parses an Internationalized Resource Identifier (IRI) reference under + RFC3987. If the IRI reference is syntactically valid, splits the string + into its components and returns an array containing the indices into the + components. + A string that contains an IRI. Can be null. + If the string is a valid IRI reference, returns an array of 10 integers. + Each of the five pairs corresponds to the start and end index of the IRI's + scheme, authority, path, query, or fragment identifier, respectively. The + scheme, authority, query, and fragment identifier, if present, will each + be given without the ending colon, the starting "//", the starting "?", + and the starting "#", respectively. If a component is absent, both indices + in that pair will be -1. If the string is null or is not a valid IRI, + returns null. + + + + + + Parses an Internationalized Resource Identifier (IRI) reference under + RFC3987. If the IRI is syntactically valid, splits the string into its + components and returns an array containing the indices into the + components. + A string representing an IRI. Can be null. + The parameter + + is a ParseMode object. + If the string is a valid IRI reference, returns an array of 10 integers. + Each of the five pairs corresponds to the start and end index of the IRI's + scheme, authority, path, query, or fragment identifier, respectively. The + scheme, authority, query, and fragment identifier, if present, will each + be given without the ending colon, the starting "//", the starting "?", + and the starting "#", respectively. If a component is absent, both indices + in that pair will be -1. If the string is null or is not a valid IRI, + returns null. + + + + + + Parses a substring that represents an Internationalized + Resource Identifier (IRI) under RFC3987. If the IRI is + syntactically valid, splits the string into its components and + returns an array containing the indices into the + components.A string that contains an IRI. Can be null.A zero-based index showing where the desired + portion of "s" begins.The length of the desired portion of "s" (but + not more than "s" 's length).Parse mode that specifies whether certain + characters are allowed when parsing IRIs and URIs.If the string is a valid IRI, returns an array of 10 + integers. Each of the five pairs corresponds to the start and end + index of the IRI's scheme, authority, path, query, or fragment + component, respectively. The scheme, authority, query, and fragment + components, if present, will each be given without the ending + colon, the starting "//", the starting "?", and the starting "#", + respectively. If a component is absent, both indices in that pair + will be -1 (an index won't be less than 0 in any other case). If + the string is null or is not a valid IRI, returns null.Either or is less than 0 or + greater than 's length, or ' s length minus is less than + .The parameter + is null. + + + + + Contains methods useful for reading and writing strings. It is designed + to have no dependencies other than the basic runtime class library. + Many of these methods work with text encoded in UTF-8, an encoding form + of the Unicode Standard which uses one byte to encode the most basic + characters and two to four bytes to encode other characters. For + example, the + GetUtf8 + method converts a text string to an array of bytes in UTF-8. + + In C# and Java, text strings are represented as sequences of 16-bit + values called + char + s. These sequences are well-formed under UTF-16, a 16-bit encoding form + of Unicode, except if they contain unpaired surrogate code points. (A + surrogate code point is used to encode supplementary characters, those + with code points U+10000 or higher, in UTF-16. A surrogate pair is a + high surrogate [U+D800 to U+DBFF] followed by a low surrogate [U+DC00 to + U+DFFF]. An unpaired surrogate code point is a surrogate not appearing + in a surrogate pair.) Many of the methods in this class allow setting + the behavior to follow when unpaired surrogate code points are found in + text strings, such as throwing an error or treating the unpaired + surrogate as a replacement character (U+FFFD). + + + + + + + Gets the Unicode code point at the given index of the string. + The parameter + + is a text string. + Index of the current position into the string. + The Unicode code point at the given position. Returns -1 if + + is less than 0, or is the string's length or greater. Returns the + replacement character (U+FFFD) if the current character is an unpaired + surrogate code point. + The parameter + + is null. + + + + + + Gets the Unicode code point at the given index of the string. + The parameter + + is a text string. + Index of the current position into the string. + Specifies what kind of value to return if the previous character is an + unpaired surrogate code point: if 0, return the replacement character + (U+FFFD); if 1, return the value of the surrogate code point; if neither 0 + nor 1, return -1. + The Unicode code point at the current position. Returns -1 if + + is less than 0, or is the string's length or greater. Returns a value as + specified under + + if the previous character is an unpaired surrogate code point. + The parameter + + is null. + + + + + + Gets the Unicode code point just before the given index of the string. + The parameter + + is a text string. + Index of the current position into the string. + The Unicode code point at the previous position. Returns -1 if + + is 0 or less, or is greater than the string's length. Returns the + replacement character (U+FFFD) if the previous character is an unpaired + surrogate code point. + The parameter + + is null. + + + + + + Gets the Unicode code point just before the given index of the string. + The parameter + + is a text string. + Index of the current position into the string. + Specifies what kind of value to return if the previous character is an + unpaired surrogate code point: if 0, return the replacement character + (U+FFFD); if 1, return the value of the surrogate code point; if neither 0 + nor 1, return -1. + The Unicode code point at the previous position. Returns -1 if + + is 0 or less, or is greater than the string's length. Returns a value as + specified under + + if the previous character is an unpaired surrogate code point. + The parameter + + is null. + + + + + + Compares two strings in Unicode code point order. Unpaired surrogate code + points are treated as individual code points. + The first string. Can be null. + The second string. Can be null. + A value indicating which string is " less" or " greater" . 0: Both + strings are equal or null. Less than 0: a is null and b isn't; or the + first code point that's different is less in A than in B; or b starts with + a and is longer than a. Greater than 0: b is null and a isn't; or the + first code point that's different is greater in A than in B; or a starts + with b and is longer than b. + + + + + + Finds the number of Unicode code points in the given text + string. Unpaired surrogate code points increase this number by 1. + This is not necessarily the length of the string in "char" + s.The parameter is a text string.The number of Unicode code points in the given + string.The parameter + is null. + + + + + Encodes a string in UTF-8 as a byte array. This method does not insert a byte-order mark (U+FEFF) at the beginning of the encoded byte array. +REMARK: It is not recommended to use Encoding.UTF8.GetBytes in .NET, or the getBytes() method in Java to do this. For instance, getBytes() encodes text strings in an unspecified character encoding. Both behaviors can be undesirable. + The parameter + + is a text string. + If true, replaces unpaired surrogate code points with the replacement + character (U+FFFD). If false, stops processing when an unpaired surrogate + code point is seen. + The string encoded in UTF-8. + The parameter + + is null. + The string contains an unpaired surrogate code point and + + is false, or an internal error occurred. + + + + + + Encodes a string in UTF-8 as a byte array. This method does not insert a byte-order mark (U+FEFF) at the beginning of the encoded byte array. +REMARK: It is not recommended to use Encoding.UTF8.GetBytes in .NET, or the getBytes() method in Java to do this. For instance, getBytes() encodes text strings in an unspecified character encoding. Both behaviors can be undesirable. + The parameter + + is a text string. + If true, replaces unpaired surrogate code points with the replacement + character (U+FFFD). If false, stops processing when an unpaired surrogate + code point is seen. + If true, replaces carriage return (CR) not followed by line feed (LF) and + LF not preceded by CR with CR-LF pairs. + The string encoded in UTF-8. + The parameter + + is null. + The string contains an unpaired surrogate code point and + + is false, or an internal error occurred. + + + + + + Calculates the number of bytes needed to encode a string in UTF-8. + The parameter + + is a text string. + If true, treats unpaired surrogate code points as having 3 UTF-8 bytes + (the UTF-8 length of the replacement character U+FFFD). + The number of bytes needed to encode the given string in UTF-8, or -1 if + the string contains an unpaired surrogate code point and + + is false. + The parameter + + is null. + + + + + + Generates a text string from a UTF-8 byte array. + A byte array containing text encoded in UTF-8. + If true, replaces invalid encoding with the replacement character + (U+FFFD). If false, stops processing when invalid UTF-8 is seen. + A string represented by the UTF-8 byte array. + The parameter + + is null. + The string is not valid UTF-8 and + + is false. + + + + + + Generates a text string from a portion of a UTF-8 byte array. + A byte array containing text encoded in UTF-8. + Offset into the byte array to start reading. + Length, in bytes, of the UTF-8 text string. + If true, replaces invalid encoding with the replacement character + (U+FFFD). If false, stops processing when invalid UTF-8 is seen. + A string represented by the UTF-8 byte array. + The parameter + + is null. + The portion of the byte array is not valid UTF-8 and + + is false. + The parameter + + is less than 0, + + is less than 0, or offset plus bytesCount is greater than the length of + "data" . + + + + + + Reads a string in UTF-8 encoding from a data stream. + A readable data stream. + The length, in bytes, of the string. If this is less than 0, this + function will read until the end of the stream. + A string builder object where the resulting string will be stored. + If true, replaces invalid encoding with the replacement character + (U+FFFD). If false, stops processing when an unpaired surrogate code point + is seen. + 0 if the entire string was read without errors, -1 if the string is not + valid UTF-8 and + + is false, or -2 if the end of the stream was reached before the last + character was read completely (which is only the case if + + is 0 or greater). + An I/O error occurred. + The parameter + + is null or + + is null. + + + + + + Reads a string in UTF-8 encoding from a byte array. + A byte array containing a UTF-8 text string. + Offset into the byte array to start reading. + Length, in bytes, of the UTF-8 text string. + A string builder object where the resulting string will be stored. + If true, replaces invalid encoding with the replacement character + (U+FFFD). If false, stops processing when invalid UTF-8 is seen. + 0 if the entire string was read without errors, or -1 if the string is + not valid UTF-8 and + + is false. + The parameter + + is null or + + is null. + The parameter + + is less than 0, + + is less than 0, or offset plus bytesCount is greater than the length of + + . + + + + + + Reads a string in UTF-8 encoding from a data stream in full and returns + that string. Replaces invalid encoding with the replacement character + (U+FFFD). + A readable data stream. + The string read. + An I/O error occurred. + The parameter + + is null. + + + + + + Reads a string in UTF-8 encoding from a data stream and returns that + string. + A readable data stream. + The length, in bytes, of the string. If this is less than 0, this + function will read until the end of the stream. + If true, replaces invalid encoding with the replacement character + (U+FFFD). If false, throws an error if an unpaired surrogate code point is + seen. + The string read. + An I/O error occurred; or, the string is not valid UTF-8 and + + is false. + The parameter + + is null. + + + + + + Returns a string with the basic upper-case letters A to Z (U+0041 to + U+005A) converted to lower-case. Other characters remain unchanged. + The parameter + + is a text string. + The converted string, or null if + + is null. + + + + + + Returns a string with the basic lower-case letters A to Z (U+0061 to + U+007A) converted to upper-case. Other characters remain unchanged. + The parameter + + is a text string. + The converted string, or null if + + is null. + + + + + + Writes a string in UTF-8 encoding to a data stream. + A string to write. + A writable data stream. + If true, replaces unpaired surrogate code points with the replacement + character (U+FFFD). If false, stops processing when an unpaired surrogate + code point is seen. + 0 if the entire string was written; or -1 if the string contains an + unpaired surrogate code point and + + is false. + The parameter + + is null or + + is null. + An I/O error occurred. + + + + + + Writes a portion of a string in UTF-8 encoding to a data stream. + A string to write. + The zero-based index where the string portion to write begins. + The length of the string portion to write. + A writable data stream. + If true, replaces unpaired surrogate code points with the replacement + character (U+FFFD). If false, stops processing when an unpaired surrogate + code point is seen. + 0 if the entire string portion was written; or -1 if the string portion + contains an unpaired surrogate code point and + + is false. + The parameter + + is null or + + is null. + The parameter + + is less than 0, + + is less than 0, or + + plus + + is greater than the string's length. + An I/O error occurred. + + + + + + Writes a portion of a string in UTF-8 encoding to a data stream. + A string to write. + The zero-based index where the string portion to write begins. + The length of the string portion to write. + A writable data stream. + If true, replaces unpaired surrogate code points with the replacement + character (U+FFFD). If false, stops processing when an unpaired surrogate + code point is seen. + If true, replaces carriage return (CR) not followed by line feed (LF) and + LF not preceded by CR with CR-LF pairs. + 0 if the entire string portion was written; or -1 if the string portion + contains an unpaired surrogate code point and + + is false. + The parameter + + is null or + + is null. + The parameter + + is less than 0, + + is less than 0, or + + plus + + is greater than the string's length. + An I/O error occurred. + + + + + + This class is largely obsolete. It will be replaced by a new version + of this class in a different namespace/package and library, called + PeterO.Numbers.EDecimal + in the + + PeterO.Numbers + + library (in .NET), or + com.upokecenter.numbers.EDecimal + in the + + com.github.peteroupc/numbers + + artifact (in Java). This new class can be used in the + CBORObject.FromObject(object) + method (by including the new library in your code, among other + things). + + Represents an arbitrary-precision decimal floating-point number. + About decimal arithmetic + + Decimal (base-10) arithmetic, such as that provided by this class, is + appropriate for calculations involving such real-world data as prices + and other sums of money, tax rates, and measurements. These calculations + often involve multiplying or dividing one decimal with another decimal, + or performing other operations on decimal numbers. Many of these + calculations also rely on rounding behavior in which the result after + rounding is a decimal number (for example, multiplying a price by a + premium rate, then rounding, should result in a decimal amount of + money). + + On the other hand, most implementations of + float + and + double + , including in C# and Java, store numbers in a binary (base-2) + floating-point format and use binary floating-point arithmetic. Many + decimal numbers can't be represented exactly in binary floating-point + format (regardless of its length). Applying binary arithmetic to numbers + intended to be decimals can sometimes lead to unintuitive results, as is + shown in the description for the FromDouble() method of this class. + + About ExtendedDecimal instances + + Each instance of this class consists of an integer mantissa and an + integer exponent, both arbitrary-precision. The value of the number + equals mantissa * 10^exponent. + + The mantissa is the value of the digits that make up a number, ignoring + the decimal point and exponent. For example, in the number 2356.78, the + mantissa is 235678. The exponent is where the "floating" decimal point + of the number is located. A positive exponent means "move it to the + right", and a negative exponent means "move it to the left." In the + example 2, 356.78, the exponent is -2, since it has 2 decimal places and + the decimal point is "moved to the left by 2." Therefore, in the + arbitrary-precision decimal representation, this number would be stored + as 235678 * 10^-2. + + The mantissa and exponent format preserves trailing zeros in the + number's value. This may give rise to multiple ways to store the same + value. For example, 1.00 and 1 would be stored differently, even though + they have the same value. In the first case, 100 * 10^-2 (100 with + decimal point moved left by 2), and in the second case, 1 * 10^0 (1 with + decimal point moved 0). + + This class also supports values for negative zero, not-a-number (NaN) + values, and infinity. + Negative zero + is generally used when a negative number is rounded to 0; it has the + same mathematical value as positive zero. + Infinity + is generally used when a non-zero number is divided by zero, or when a + very high number can't be represented in a given exponent range. + Not-a-number + is generally used to signal errors. + + Errors and Exceptions + + Passing a signaling NaN to any arithmetic operation shown here will + signal the flag FlagInvalid and return a quiet NaN, even if another + operand to that operation is a quiet NaN, unless noted otherwise. + + Passing a quiet NaN to any arithmetic operation shown here will return + a quiet NaN, unless noted otherwise. Invalid operations will also return + a quiet NaN, as stated in the individual methods. + + Unless noted otherwise,passing a null arbitrary-precision decimal + argument to any method here will throw an exception. + + When an arithmetic operation signals the flag FlagInvalid, + FlagOverflow, or FlagDivideByZero, it will not throw an exception too, + unless the flag's trap is enabled in the precision context (see + EContext's Traps property). + + If an operation requires creating an intermediate value that might be + too big to fit in memory (or might require more than 2 gigabytes of + memory to store -- due to the current use of a 32-bit integer internally + as a length), the operation may signal an invalid-operation flag and + return not-a-number (NaN). In certain rare cases, the CompareTo method + may throw OutOfMemoryException (called OutOfMemoryError in Java) in the + same circumstances. + + Serialization + + An arbitrary-precision decimal value can be serialized (converted to a + stable format) in one of the following ways: + + + By calling the toString() method, which will always return distinct + strings for distinct arbitrary-precision decimal values. + + By calling the UnsignedMantissa, Exponent, and IsNegative properties, + and calling the IsInfinity, IsQuietNaN, and IsSignalingNaN methods. + The return values combined will uniquely identify a particular + arbitrary-precision decimal value. + + + Thread safety + + Instances of this class are immutable, so they are inherently safe for + use by multiple threads. Multiple instances of this object with the same + properties are interchangeable, so they should not be compared using the + "==" operator (which might only check if each side of the operator is + the same instance). + + Comparison considerations + + This class's natural ordering (under the CompareTo method) is not + consistent with the Equals method. This means that two values that + compare as equal under the CompareTo method might not be equal under the + Equals method. The CompareTo method compares the mathematical values of + the two instances passed to it (and considers two different NaN values + as equal), while two instances with the same mathematical value, but + different exponents, will be considered unequal under the Equals method. + + + + + + + + Compares this extended decimal to another. + The parameter + + is an ExtendedDecimal object. + Less than 0 if this value is less than, 0 if equal to, or greater than 0 + if greater than the other extended decimal. + + + + + + Creates a number with the value + exponent*10^mantissa.The un-scaled value.The decimal exponent.An arbitrary-precision decimal number.The parameter + or is + null. + + + + + Determines whether this object's mantissa and exponent are equal to those + of another object. + An arbitrary-precision decimal number. + true + if this object's mantissa and exponent are equal to those of another + object; otherwise, false + . + + + + + + Determines whether this object's mantissa and exponent are equal to those + of another object and that other object is an arbitrary-precision decimal + number. + The parameter + + is an arbitrary object. + true + if the objects are equal; otherwise, false + . + + + + + + Gets this object's exponent. This object's value will be + an integer if the exponent is positive or zero.This object's exponent. This object's value will be an + integer if the exponent is positive or zero. + + + + + Creates a decimal number from a text string that + represents a number. See FromString(String, int, int, + EContext) for more information.A string that represents a number.An arbitrary-precision decimal number with the same value + as the given string.The parameter + is null.The parameter is not a correctly formatted number + string. + + + + + Calculates this object's hash code. No application or process IDs are + used in the hash code calculation. + This object's hash code. + + + + + + Gets a value indicating whether this object is positive or negative + infinity. + true + if this object is positive or negative infinity; otherwise, false + . + + + + + + Gets a value indicating whether this object is not a number (NaN). + true + if this object is not a number (NaN); otherwise, false + . + + + + + + Gets a value indicating whether this object is negative, + including negative zero.true If this object is negative, including negative + zero; otherwise, . false. + + + + + Gets a value indicating whether this object is a quiet not-a-number + value. + true + if this object is a quiet not-a-number value; otherwise, false + . + + + + + + Gets this object's un-scaled value.This object's un-scaled value. Will be negative if this + object's value is negative (including a negative NaN). + + + + + A not-a-number value. + + + + + + Negative infinity, less than any other number. + + + + + + Represents the number negative zero. + + + + + + Represents the number 1. + + + + + + Positive infinity, greater than any other number. + + + + + + Gets this value's sign: -1 if negative; 1 if positive; 0 + if zero.This value's sign: -1 if negative; 1 if positive; 0 if + zero. + + + + + A not-a-number value that signals an invalid operation flag when it's + passed as an argument to any arithmetic operation in arbitrary-precision + decimal. + + + + + + Represents the number 10. + + + + + + Converts this value to a 64-bit floating-point number. The half-even + rounding mode is used. + If this value is a NaN, sets the high bit of the 64-bit floating point + number's mantissa for a quiet NaN, and clears it for a signaling NaN. + Then the next highest bit of the mantissa is cleared for a quiet NaN, + and set for a signaling NaN. Then the other bits of the mantissa are set + to the lowest bits of this object's unsigned mantissa. + + The closest 64-bit floating-point number to this value. The return value + can be positive infinity or negative infinity if this value exceeds the + range of a 64-bit floating point number. + + + + + + Converts this value to a 32-bit floating-point number. The half-even + rounding mode is used. + If this value is a NaN, sets the high bit of the 32-bit floating point + number's mantissa for a quiet NaN, and clears it for a signaling NaN. + Then the next highest bit of the mantissa is cleared for a quiet NaN, + and set for a signaling NaN. Then the other bits of the mantissa are set + to the lowest bits of this object's unsigned mantissa. + + The closest 32-bit floating-point number to this value. The return value + can be positive infinity or negative infinity if this value exceeds the + range of a 32-bit floating point number. + + + + + + Converts this value to a string. Returns a value compatible with this + class's FromString method. + A string representation of this object. + + + + + + Gets the absolute value of this object's un-scaled + value.The absolute value of this object's un-scaled value. + + + + + Represents the number 0. + + + + + + This class is largely obsolete. It will be replaced by a new version + of this class in a different namespace/package and library, called + PeterO.Numbers.EFloat + in the + + PeterO.Numbers + + library (in .NET), or + com.upokecenter.numbers.EFloat + in the + + com.github.peteroupc/numbers + + artifact (in Java). This new class can be used in the + CBORObject.FromObject(object) + method (by including the new library in your code, among other + things). + + Represents an arbitrary-precision binary floating-point number. + Consists of an integer mantissa and an integer exponent, both + arbitrary-precision. The value of the number equals mantissa * + 2^exponent. This class also supports values for negative zero, + not-a-number (NaN) values, and infinity. + + Passing a signaling NaN to any arithmetic operation shown here will + signal the flag FlagInvalid and return a quiet NaN, even if another + operand to that operation is a quiet NaN, unless noted otherwise. + + Passing a quiet NaN to any arithmetic operation shown here will return + a quiet NaN, unless noted otherwise. + + Unless noted otherwise,passing a null arbitrary-precision binary float + argument to any method here will throw an exception. + + When an arithmetic operation signals the flag FlagInvalid, + FlagOverflow, or FlagDivideByZero, it will not throw an exception too, + unless the operation's trap is enabled in the precision context (see + PrecisionContext's Traps property). + + An arbitrary-precision binary float value can be serialized in one of + the following ways: + + + By calling the toString() method. However, not all strings can be + converted back to an arbitrary-precision binary float without loss, + especially if the string has a fractional part. + + By calling the UnsignedMantissa, Exponent, and IsNegative properties, + and calling the IsInfinity, IsQuietNaN, and IsSignalingNaN methods. + The return values combined will uniquely identify a particular + arbitrary-precision binary float value. + + + If an operation requires creating an intermediate value that might be + too big to fit in memory (or might require more than 2 gigabytes of + memory to store -- due to the current use of a 32-bit integer internally + as a length), the operation may signal an invalid-operation flag and + return not-a-number (NaN). In certain rare cases, the CompareTo method + may throw OutOfMemoryException (called OutOfMemoryError in Java) in the + same circumstances. + + Thread safety: + Instances of this class are immutable, so they are inherently safe for + use by multiple threads. Multiple instances of this object with the same + properties are interchangeable, so they should not be compared using the + "==" operator (which might only check if each side of the operator is + the same instance). + + + + + + + Compares this extended float to another. + An extended float to compare this one with. + Less than 0 if this value is less than, 0 if equal to, or greater than 0 + if greater than the other value. + + + + + + Creates a number with the value + exponent*2^mantissa.The un-scaled value.The binary exponent.An arbitrary-precision binary float.The parameter + or is + null. + + + + + Creates a number with the value exponent*2^mantissa. + The un-scaled value. + The binary exponent. + An arbitrary-precision binary float. + + + + + + Determines whether this object's mantissa and exponent are + equal to those of another object.An arbitrary-precision binary float.true if this object's mantissa and exponent are + equal to those of another object; otherwise, false + .The parameter + is null. + + + + + Determines whether this object's mantissa and exponent are equal to those + of another object and that other object is an arbitrary-precision decimal + number. + The parameter + + is an arbitrary object. + true + if the objects are equal; otherwise, false + . + + + + + + Determines whether this object's mantissa and exponent are + equal to those of another object.An arbitrary-precision binary + float.true if this object's mantissa and exponent are + equal to those of another object; otherwise, false + .The parameter + is null. + + + + + Gets this object's exponent. This object's value will be + an integer if the exponent is positive or zero.This object's exponent. This object's value will be an + integer if the exponent is positive or zero. + + + + + Creates a binary float from a text string that represents a number. + A text string containing the number to convert. + The parsed number, converted to arbitrary-precision binary float. + + + + + + Creates a binary float from a text string that represents + a number. Note that if the string contains a negative exponent, the + resulting value might not be exact, in which case the resulting + binary float will be an approximation of this decimal number's + value. (NOTE: This documentation previously said the binary float + will contain enough precision to accurately convert it to a 32-bit + or 64-bit floating point number. Due to double rounding, this will + generally not be the case for certain numbers converted from + decimal to ExtendedFloat via this method and in turn converted to + double or float.) + The format of the string generally consists of: + + An optional plus sign ("+" , U+002B) or minus sign ("-", + U+002D) (if '-' , the value is negative.) + One or more digits, with a single optional decimal point + after the first digit and before the last digit. + Optionally, "E+"/"e+" (positive exponent) or "E-"/"e-" + (negative exponent) plus one or more digits specifying the + exponent. + The string can also be "-INF", "-Infinity", "Infinity", + "INF", quiet NaN ("NaN") followed by any number of digits, or + signaling NaN ("sNaN") followed by any number of digits, all in any + combination of upper and lower case. + All characters mentioned above are the corresponding + characters in the Basic Latin range. In particular, the digits must + be the basic digits 0 to 9 (U+0030 to U+0039). The string is not + allowed to contain white space characters, including + spaces.The parameter is a text + string.A zero-based index showing where the desired + portion of begins.The length, in code units, of the desired + portion of (but not more than 's length).A PrecisionContext object specifying the + precision, rounding, and exponent range to apply to the parsed + number. Can be null.The parsed number, converted to arbitrary-precision binary + float.The parameter + is null.Either or is less than 0 or + greater than 's length, or ' s length minus is less + than . + + + + + Calculates this object's hash code. No application or process IDs are + used in the hash code calculation. + This object's hash code. + + + + + + Gets a value indicating whether this object is positive or negative + infinity. + true + if this object is positive or negative infinity; otherwise, false + . + + + + + + Returns whether this object is a not-a-number value. + true + if this object is a not-a-number value; otherwise, false + . + + + + + + Gets a value indicating whether this object is negative, + including negative zero.true If this object is negative, including negative + zero; otherwise, . false. + + + + + Returns whether this object is negative infinity. + true + if this object is negative infinity; otherwise, false + . + + + + + + Returns whether this object is positive infinity. + true + if this object is positive infinity; otherwise, false + . + + + + + + Gets a value indicating whether this object is a quiet not-a-number + value. + true + if this object is a quiet not-a-number value; otherwise, false + . + + + + + + Gets a value indicating whether this object is a signaling not-a-number + value. + true + if this object is a signaling not-a-number value; otherwise, false + . + + + + + + Gets this object's un-scaled value.This object's un-scaled value. Will be negative if this + object's value is negative (including a negative NaN). + + + + + A not-a-number value. + + + + + + Negative infinity, less than any other number. + + + + + + Represents the number negative zero. + + + + + + Represents the number 1. + + + + + + Positive infinity, greater than any other number. + + + + + + Gets this value's sign: -1 if negative; 1 if positive; 0 + if zero.This value's sign: -1 if negative; 1 if positive; 0 if + zero. + + + + + A not-a-number value that signals an invalid operation flag when it's + passed as an argument to any arithmetic operation in arbitrary-precision + binary float. + + + + + + Represents the number 10. + + + + + + Converts this value to a string. + A string representation of this object. The value is converted to decimal + and the decimal form of this number's value is returned. + + + + + + Gets the absolute value of this object's un-scaled + value.The absolute value of this object's un-scaled value. + + + + + Represents the number 0. + + + + + + This class is largely obsolete. It will be replaced by a new version + of this class in a different namespace/package and library, called + PeterO.Numbers.ERational + in the + + PeterO.Numbers + + library (in .NET), or + com.upokecenter.numbers.ERational + in the + + com.github.peteroupc/numbers + + artifact (in Java). This new class can be used in the + CBORObject.FromObject(object) + method (by including the new library in your code, among other + things). + + Arbitrary-precision rational number. This class can't be inherited; this + is a change in version 2.0 from previous versions, where the class was + inadvertently left inheritable. + Thread safety: + Instances of this class are immutable, so they are inherently safe for + use by multiple threads. Multiple instances of this object with the same + properties are interchangeable, so they should not be compared using the + "==" operator (which might only check if each side of the operator is + the same instance). + + + + + + + Initializes a new instance of the class.An arbitrary-precision integer.Another arbitrary-precision + integer.The parameter + or is + null. + + + + + Compares this value to another. + The parameter + + is an ExtendedRational object. + Less than 0 if this value is less than, 0 if equal to, or greater than 0 + if greater than the other value. + + + + + + Creates a rational number with the given numerator and + denominator.An arbitrary-precision integer.Another arbitrary-precision + integer.An arbitrary-precision rational number.The parameter + or is + null. + + + + + Creates a rational number with the given numerator and denominator. + The parameter + + is a 32-bit signed integer. + The parameter + + is a 32-bit signed integer. + An arbitrary-precision rational number. + + + + + + Gets this object's denominator.This object's denominator. + + + + + Checks whether this and another value are equal. + The parameter + + is an ExtendedRational object. + Either + true + or + false + . + + + + + + Checks whether this and another value are equal. + The parameter + + is an arbitrary object. + Either + true + or + false + . + + + + + + Calculates the hash code for this object. No application or process IDs + are used in the hash code calculation. + A 32-bit signed integer. + + + + + + Gets a value indicating whether this object is finite (not + infinity or NaN).true If this object is finite (not infinity or NaN); + otherwise, . false. + + + + + Gets a value indicating whether this object's value is + negative (including negative zero).true If this object's value is negative; otherwise, . + false. + + + + + Gets a value indicating whether this object's value equals + 0.true If this object's value equals 0; otherwise, . + false. + + + + + A not-a-number value. + + + + + + Negative infinity, less than any other number. + + + + + + A rational number for negative zero. + + + + + + Gets this object's numerator.This object's numerator. If this object is a not-a-number + value, returns the diagnostic information (which will be negative + if this object is negative). + + + + + The rational number one. + + + + + + Positive infinity, greater than any other number. + + + + + + Gets the sign of this rational number. + Zero if this value is zero or negative zero; -1 if this value is less + than 0; and 1 if this value is greater than 0. + + + + + + A signaling not-a-number value. + + + + + + The rational number ten. + + + + + + Converts this object to a text string. + A string representation of this object. The result can be Infinity, NaN, + or sNaN (with a minus sign before it for negative values), or a number of + the following form: [-]numerator/denominator. + + + + + + Gets this object's numerator with the sign + removed.This object's numerator. If this object is a not-a-number + value, returns the diagnostic information. + + + + + A rational number for zero. + + + + + + A precision context. + + + + + + Initializes a new instance of the class. HasFlags will be set to false. + The maximum number of digits a number can have, or 0 for an unlimited + number of digits. + The rounding mode to use when a number can't fit the given precision. + The minimum exponent. + The maximum exponent. + Whether to clamp a number's significand to the given maximum precision + (if it isn't zero) while remaining within the exponent range. + + + + + + Gets a string representation of this object. Note that the format is not + intended to be parsed and may change at any time. + A string representation of this object. + + + + +Contains utility methods for processing Uniform Resource Identifiers + (URIs) and Internationalized Resource Identifiers (IRIs) under RFC3986 and + RFC3987, respectively. In the following documentation, URIs and IRIs + include URI references and IRI references, for convenience. + + + +Specifies whether certain characters are allowed when parsing IRIs and + URIs. + + + +The rules only check for the appropriate delimiters when splitting the + path, without checking if all the characters in each component are valid. + Even with this mode, strings with unpaired surrogate code points are + considered invalid. + + + +The rules follow the syntax for parsing IRIs. In particular, many code + points outside the Basic Latin range (U+0000 to U+007F) are allowed. + Strings with unpaired surrogate code points are considered invalid. + + + +The rules only check for the appropriate delimiters when splitting the + path, without checking if all the characters in each component are valid. + Unpaired surrogate code points are treated as though they were replacement + characters instead for the purposes of these rules, so that strings with + those code points are not considered invalid strings. + + + +The rules only check for the appropriate delimiters when splitting the + path, without checking if all the characters in each component are valid. + Code points outside the Basic Latin range (U+0000 to U+007F) are not + allowed. + + + +The rules follow the syntax for parsing IRIs, except that code points + outside the Basic Latin range (U+0000 to U+007F) are not allowed. + + + +A string possibly containing escaped characters, or null if s is null. + + Escapes characters that cannot appear in URIs or IRIs. The function is + idempotent; that is, calling the function again on the result with the + same mode doesn't change the result. + + A string to escape. + + The parameter + + is a 32-bit signed integer. + + + +Determines whether the string is a valid IRI with a scheme component. This + can be used to check for relative IRI references. + The following cases return true: + + xx-x:mm example:/ww + + The following cases return false: + x@y:/z /x/y/z example.xyz + + + A string representing an IRI to check. + + true + if the string is a valid IRI with a scheme component; otherwise, false + . + + + +Determines whether the string is a valid URI with a scheme component. This + can be used to check for relative URI references. The following cases + return true: + http://example/z xx-x:mm example:/ww + + The following cases return false: + x@y:/z /x/y/z example.xyz + + + A string representing an IRI to check. + + true + if the string is a valid URI with a scheme component; otherwise, false + . + + + +Determines whether the substring is a valid CURIE reference under RDFA + 1.1. (The CURIE reference is the part after the colon.). + + A string containing a CURIE reference. Can be null. + + A zero-based index showing where the desired portion of "s" begins. + + The number of elements in the desired portion of "s" (but not more than + "s" 's length). + + true + if the substring is a valid CURIE reference under RDFA 1; otherwise, false + . Returns false if + + is null. + + Either + + or + + is less than 0 or greater than + + 's length, or + + 's length minus + + is less than + + . + + + +Resolves a URI or IRI relative to another URI or IRI. + + A string representing a URI or IRI reference. Example: + dir/file.txt + . + + A string representing an absolute URI reference. Example: + http://example.com/my/path/ + . + + The resolved IRI, or null if + + is null or is not a valid IRI. If base is null or is not a valid IRI, + returns refValue. Example: http://example.com/my/path/dir/file.txt. + + + +Resolves a URI or IRI relative to another URI or IRI. + + A string representing a URI or IRI reference. Example: + dir/file.txt + . + + A string representing an absolute URI reference. Example: + http://example.com/my/path/ + . + + Parse mode that specifies whether certain characters are allowed when + parsing IRIs and URIs. + + The resolved IRI, or null if + + is null or is not a valid IRI. If base is null or is not a valid IRI, + returns refValue. + + + +Parses an Internationalized Resource Identifier (IRI) reference under + RFC3987. If the IRI reference is syntactically valid, splits the string + into its components and returns an array containing the indices into the + components. + If the string is a valid IRI reference, returns an array of 10 integers. + Each of the five pairs corresponds to the start and end index of the + IRI's scheme, authority, path, query, or fragment identifier, + respectively. If a component is absent, both indices in that pair will + be -1. If the string is null or is not a valid IRI, returns null. + + + A string that contains an IRI. + + + +Parses an Internationalized Resource Identifier (IRI) reference under + RFC3987. If the IRI is syntactically valid, splits the string into its + components and returns an array containing the indices into the + components. + + A string representing an IRI. Can be null. + + The parameter + + is a ParseMode object. + + If the string is a valid IRI reference, returns an array of 10 integers. + Each of the five pairs corresponds to the start and end index of the IRI's + scheme, authority, path, query, or fragment identifier, respectively. If a + component is absent, both indices in that pair will be -1. If the string + is null or is not a valid IRI, returns null. + + + +Parses a substring that represents an Internationalized Resource + Identifier (IRI) under RFC3987. If the IRI is syntactically valid, splits + the string into its components and returns an array containing the indices + into the components. + + A string that contains an IRI. + + A zero-based index showing where the desired portion of "s" begins. + + The length of the desired portion of "s" (but not more than "s" 's + length). + + Parse mode that specifies whether certain characters are allowed when + parsing IRIs and URIs. + + If the string is a valid IRI, returns an array of 10 integers. Each of the + five pairs corresponds to the start and end index of the IRI's scheme, + authority, path, query, or fragment identifier, respectively. If a + component is absent, both indices in that pair will be -1 (an index won't + be less than 0 in any other case). If the string is null or is not a valid + IRI, returns null. + + Either + + or + + is less than 0 or greater than + + 's length, or + + 's length minus + + is less than + + . + + The parameter + + is null. + + + + + This class is obsolete. It will be replaced by a new version of this + class in a different namespace/package and library, called + PeterO.Numbers.ERounding + in the + PeterO.ERounding + library (in .NET), or + com.upokecenter.numbers.EFloat + in the + + com.github.peteroupc/numbers + + artifact (in Java). + + Specifies the mode to use when "shortening" numbers that otherwise can't + fit a given number of digits, so that the shortened number has about the + same value. This "shortening" is known as rounding. + + + + + + If there is a fractional part, the number is rounded to the highest + representable number that's closest to it. + + + + + + The fractional part is discarded (the number is truncated). + + + + + + If there is a fractional part, the number is rounded to the lowest + representable number that's closest to it. + + + + + + Rounded to the nearest number; if the fractional part is exactly half, it + is discarded. + + + + + + Rounded to the nearest number; if the fractional part is exactly half, + the number is rounded to the closest representable number that is even. + This is sometimes also known as "banker's rounding". + + + + + + Rounded to the nearest number; if the fractional part is exactly half, + the number is rounded to the closest representable number away from zero. + This is the most familiar rounding mode for many people. + + + + + + If there is a fractional part and the whole number part is even, the + number is rounded to the closest representable odd number away from zero. + + + + + + For binary floating point numbers, this is the same as Odd. For other + bases (including decimal numbers), this is the same as ZeroFiveUp. This + rounding mode is useful for rounding intermediate results at a slightly + higher precision (at least 2 bits more for binary) than the final + precision. + + + + + + Indicates that rounding will not be used. If rounding is required, the + rounding operation will report an error. + + + + + + If there is a fractional part, the number is rounded to the closest + representable number away from zero. + + + + + + If there is a fractional part and if the last digit before rounding is 0 + or half the radix, the number is rounded to the closest representable + number away from zero; otherwise the fractional part is discarded. In + overflow, the fractional part is always discarded. + + + + + + This class is obsolete. It will be replaced by a new version of this + class in a different namespace/package and library, called + PeterO.Numbers.ETrapException + in the + + PeterO.Numbers + + library (in .NET), or + com.upokecenter.numbers.ETrapException + in the + + com.github.peteroupc/numbers + + artifact (in Java). + + Exception thrown for arithmetic trap errors. + + + + + + Initializes a new instance of the class. + A flag that specifies the kind of error (PrecisionContext.FlagXXX). This + will only be one flag, such as FlagInexact or FlagSubnormal. + A context object for arbitrary-precision arithmetic settings. + The desired result of the operation that caused the trap, such as an + ExtendedDecimal + or + ExtendedFloat + . + + + + + + Gets the precision context used during the operation that triggered the + trap. May be null. + The precision context used during the operation that triggered the trap. + May be null. + + + + + + Gets the flag that specifies the kind of error + (PrecisionContext.FlagXXX). This will only be one flag, such as + FlagInexact or FlagSubnormal. + The flag that specifies the kind of error (PrecisionContext.FlagXXX). + This will only be one flag, such as FlagInexact or FlagSubnormal. + + + + + + Gets the defined result of the operation that caused the trap. + The defined result of the operation that caused the trap. + + + + From 38ea570523e1d5dff120fb179045ad08b13f46a3 Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Fri, 19 Oct 2018 10:38:39 -0400 Subject: [PATCH 02/10] Squashed 'Chaos.NaCl/' content from commit 16ae652 git-subtree-dir: Chaos.NaCl git-subtree-split: 16ae652070749678fd74f453bbe0c8f00a9a2f7b --- Chaos.NaCl-Portable.csproj | 111 ++ Chaos.NaCl.csproj | 117 ++ CryptoBytes.cs | 190 +++ Ed25519.cs | 147 ++ Internal/Array16.cs | 27 + Internal/Array8.cs | 18 + Internal/ByteIntegerConverter.cs | 416 +++++ Internal/Ed25519Ref10/FieldElement.cs | 36 + Internal/Ed25519Ref10/GroupElement.cs | 63 + Internal/Ed25519Ref10/base.cs | 1357 +++++++++++++++++ Internal/Ed25519Ref10/base2.cs | 50 + Internal/Ed25519Ref10/d.cs | 9 + Internal/Ed25519Ref10/d2.cs | 9 + Internal/Ed25519Ref10/fe_0.cs | 12 + Internal/Ed25519Ref10/fe_1.cs | 13 + Internal/Ed25519Ref10/fe_add.cs | 63 + Internal/Ed25519Ref10/fe_cmov.cs | 70 + Internal/Ed25519Ref10/fe_cswap.cs | 78 + Internal/Ed25519Ref10/fe_frombytes.cs | 122 ++ Internal/Ed25519Ref10/fe_invert.cs | 179 +++ Internal/Ed25519Ref10/fe_isnegative.cs | 22 + Internal/Ed25519Ref10/fe_isnonzero.cs | 37 + Internal/Ed25519Ref10/fe_mul.cs | 258 ++++ Internal/Ed25519Ref10/fe_mul121666.cs | 76 + Internal/Ed25519Ref10/fe_neg.cs | 50 + Internal/Ed25519Ref10/fe_pow22523.cs | 175 +++ Internal/Ed25519Ref10/fe_sq.cs | 153 ++ Internal/Ed25519Ref10/fe_sq2.cs | 164 ++ Internal/Ed25519Ref10/fe_sub.cs | 63 + Internal/Ed25519Ref10/fe_tobytes.cs | 154 ++ Internal/Ed25519Ref10/ge_add.cs | 113 ++ Internal/Ed25519Ref10/ge_double_scalarmult.cs | 118 ++ Internal/Ed25519Ref10/ge_frombytes.cs | 54 + Internal/Ed25519Ref10/ge_madd.cs | 105 ++ Internal/Ed25519Ref10/ge_msub.cs | 104 ++ Internal/Ed25519Ref10/ge_p1p1_to_p2.cs | 18 + Internal/Ed25519Ref10/ge_p1p1_to_p3.cs | 18 + Internal/Ed25519Ref10/ge_p2_0.cs | 14 + Internal/Ed25519Ref10/ge_p2_dbl.cs | 90 ++ Internal/Ed25519Ref10/ge_p3_0.cs | 15 + Internal/Ed25519Ref10/ge_p3_dbl.cs | 17 + Internal/Ed25519Ref10/ge_p3_to_cached.cs | 18 + Internal/Ed25519Ref10/ge_p3_to_p2.cs | 17 + Internal/Ed25519Ref10/ge_p3_tobytes.cs | 20 + Internal/Ed25519Ref10/ge_precomp_0.cs | 14 + Internal/Ed25519Ref10/ge_scalarmult_base.cs | 113 ++ Internal/Ed25519Ref10/ge_sub.cs | 114 ++ Internal/Ed25519Ref10/ge_tobytes.cs | 20 + Internal/Ed25519Ref10/keypair.cs | 23 + Internal/Ed25519Ref10/open.cs | 80 + Internal/Ed25519Ref10/sc_clamp.cs | 14 + Internal/Ed25519Ref10/sc_mul_add.cs | 374 +++++ Internal/Ed25519Ref10/sc_reduce.cs | 263 ++++ Internal/Ed25519Ref10/scalarmult.cs | 205 +++ Internal/Ed25519Ref10/sign.cs | 81 + Internal/Ed25519Ref10/sqrtm1.cs | 9 + Internal/InternalAssert.cs | 13 + Internal/Poly1305Donna.cs | 154 ++ Internal/Salsa/Salsa20.cs | 45 + Internal/Salsa/SalsaCore.cs | 263 ++++ Internal/Salsa/replace regex.txt | 2 + Internal/Sha512Internal.cs | 447 ++++++ MontgomeryCurve25519.cs | 142 ++ OneTimeAuth.cs | 19 + Poly1305.cs | 97 ++ Properties/AssemblyInfo.cs | 30 + Properties/AssemblyInfoFull.cs | 9 + Sha512.cs | 132 ++ XSalsa20Poly1305.cs | 247 +++ 69 files changed, 7870 insertions(+) create mode 100644 Chaos.NaCl-Portable.csproj create mode 100644 Chaos.NaCl.csproj create mode 100644 CryptoBytes.cs create mode 100644 Ed25519.cs create mode 100644 Internal/Array16.cs create mode 100644 Internal/Array8.cs create mode 100644 Internal/ByteIntegerConverter.cs create mode 100644 Internal/Ed25519Ref10/FieldElement.cs create mode 100644 Internal/Ed25519Ref10/GroupElement.cs create mode 100644 Internal/Ed25519Ref10/base.cs create mode 100644 Internal/Ed25519Ref10/base2.cs create mode 100644 Internal/Ed25519Ref10/d.cs create mode 100644 Internal/Ed25519Ref10/d2.cs create mode 100644 Internal/Ed25519Ref10/fe_0.cs create mode 100644 Internal/Ed25519Ref10/fe_1.cs create mode 100644 Internal/Ed25519Ref10/fe_add.cs create mode 100644 Internal/Ed25519Ref10/fe_cmov.cs create mode 100644 Internal/Ed25519Ref10/fe_cswap.cs create mode 100644 Internal/Ed25519Ref10/fe_frombytes.cs create mode 100644 Internal/Ed25519Ref10/fe_invert.cs create mode 100644 Internal/Ed25519Ref10/fe_isnegative.cs create mode 100644 Internal/Ed25519Ref10/fe_isnonzero.cs create mode 100644 Internal/Ed25519Ref10/fe_mul.cs create mode 100644 Internal/Ed25519Ref10/fe_mul121666.cs create mode 100644 Internal/Ed25519Ref10/fe_neg.cs create mode 100644 Internal/Ed25519Ref10/fe_pow22523.cs create mode 100644 Internal/Ed25519Ref10/fe_sq.cs create mode 100644 Internal/Ed25519Ref10/fe_sq2.cs create mode 100644 Internal/Ed25519Ref10/fe_sub.cs create mode 100644 Internal/Ed25519Ref10/fe_tobytes.cs create mode 100644 Internal/Ed25519Ref10/ge_add.cs create mode 100644 Internal/Ed25519Ref10/ge_double_scalarmult.cs create mode 100644 Internal/Ed25519Ref10/ge_frombytes.cs create mode 100644 Internal/Ed25519Ref10/ge_madd.cs create mode 100644 Internal/Ed25519Ref10/ge_msub.cs create mode 100644 Internal/Ed25519Ref10/ge_p1p1_to_p2.cs create mode 100644 Internal/Ed25519Ref10/ge_p1p1_to_p3.cs create mode 100644 Internal/Ed25519Ref10/ge_p2_0.cs create mode 100644 Internal/Ed25519Ref10/ge_p2_dbl.cs create mode 100644 Internal/Ed25519Ref10/ge_p3_0.cs create mode 100644 Internal/Ed25519Ref10/ge_p3_dbl.cs create mode 100644 Internal/Ed25519Ref10/ge_p3_to_cached.cs create mode 100644 Internal/Ed25519Ref10/ge_p3_to_p2.cs create mode 100644 Internal/Ed25519Ref10/ge_p3_tobytes.cs create mode 100644 Internal/Ed25519Ref10/ge_precomp_0.cs create mode 100644 Internal/Ed25519Ref10/ge_scalarmult_base.cs create mode 100644 Internal/Ed25519Ref10/ge_sub.cs create mode 100644 Internal/Ed25519Ref10/ge_tobytes.cs create mode 100644 Internal/Ed25519Ref10/keypair.cs create mode 100644 Internal/Ed25519Ref10/open.cs create mode 100644 Internal/Ed25519Ref10/sc_clamp.cs create mode 100644 Internal/Ed25519Ref10/sc_mul_add.cs create mode 100644 Internal/Ed25519Ref10/sc_reduce.cs create mode 100644 Internal/Ed25519Ref10/scalarmult.cs create mode 100644 Internal/Ed25519Ref10/sign.cs create mode 100644 Internal/Ed25519Ref10/sqrtm1.cs create mode 100644 Internal/InternalAssert.cs create mode 100644 Internal/Poly1305Donna.cs create mode 100644 Internal/Salsa/Salsa20.cs create mode 100644 Internal/Salsa/SalsaCore.cs create mode 100644 Internal/Salsa/replace regex.txt create mode 100644 Internal/Sha512Internal.cs create mode 100644 MontgomeryCurve25519.cs create mode 100644 OneTimeAuth.cs create mode 100644 Poly1305.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/AssemblyInfoFull.cs create mode 100644 Sha512.cs create mode 100644 XSalsa20Poly1305.cs diff --git a/Chaos.NaCl-Portable.csproj b/Chaos.NaCl-Portable.csproj new file mode 100644 index 00000000..a929c239 --- /dev/null +++ b/Chaos.NaCl-Portable.csproj @@ -0,0 +1,111 @@ + + + + + 10.0 + Debug + AnyCPU + {992710C3-DFC9-4CC1-A38B-B3F7E4A41B1C} + Library + Properties + Chaos.NaCl + Chaos.NaCl-Portable + v4.0 + Profile1 + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Chaos.NaCl.csproj b/Chaos.NaCl.csproj new file mode 100644 index 00000000..5c67ef1b --- /dev/null +++ b/Chaos.NaCl.csproj @@ -0,0 +1,117 @@ + + + + + Debug + AnyCPU + {AE28FD14-7985-4707-A963-C94B8597AE50} + Library + Properties + Chaos.NaCl + Chaos.NaCl + v2.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + SecurityRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + default + MinimumRecommendedRules.ruleset + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CryptoBytes.cs b/CryptoBytes.cs new file mode 100644 index 00000000..477576d6 --- /dev/null +++ b/CryptoBytes.cs @@ -0,0 +1,190 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Chaos.NaCl +{ + public static class CryptoBytes + { + public static bool ConstantTimeEquals(byte[] x, byte[] y) + { + if (x == null) + throw new ArgumentNullException("x"); + if (y == null) + throw new ArgumentNullException("y"); + if (x.Length != y.Length) + throw new ArgumentException("x.Length must equal y.Length"); + return InternalConstantTimeEquals(x, 0, y, 0, x.Length) != 0; + } + + public static bool ConstantTimeEquals(ArraySegment x, ArraySegment y) + { + if (x.Array == null) + throw new ArgumentNullException("x.Array"); + if (y.Array == null) + throw new ArgumentNullException("y.Array"); + if (x.Count != y.Count) + throw new ArgumentException("x.Count must equal y.Count"); + + return InternalConstantTimeEquals(x.Array, x.Offset, y.Array, y.Offset, x.Count) != 0; + } + + public static bool ConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length) + { + if (x == null) + throw new ArgumentNullException("x"); + if (xOffset < 0) + throw new ArgumentOutOfRangeException("xOffset", "xOffset < 0"); + if (y == null) + throw new ArgumentNullException("y"); + if (yOffset < 0) + throw new ArgumentOutOfRangeException("yOffset", "yOffset < 0"); + if (length < 0) + throw new ArgumentOutOfRangeException("length", "length < 0"); + if (x.Length - xOffset < length) + throw new ArgumentException("xOffset + length > x.Length"); + if (y.Length - yOffset < length) + throw new ArgumentException("yOffset + length > y.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)); + } + + public static void Wipe(byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + InternalWipe(data, 0, data.Length); + } + + public static void Wipe(byte[] data, int offset, int count) + { + if (data == null) + throw new ArgumentNullException("data"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (count < 0) + throw new ArgumentOutOfRangeException("count", "Requires count >= 0"); + if ((uint)offset + (uint)count > (uint)data.Length) + throw new ArgumentException("Requires offset + count <= data.Length"); + InternalWipe(data, offset, count); + } + + public static void Wipe(ArraySegment data) + { + if (data.Array == null) + throw new ArgumentNullException("data.Array"); + 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(ref T data) + where T : struct + { + data = default(T); + } + + // constant time hex conversion + // see http://stackoverflow.com/a/14333437/445517 + // + // An explanation of the weird bit fiddling: + // + // 1. `bytes[i] >> 4` extracts the high nibble of a byte + // `bytes[i] & 0xF` extracts the low nibble of a byte + // 2. `b - 10` + // is `< 0` for values `b < 10`, which will become a decimal digit + // is `>= 0` for values `b > 10`, which will become a letter from `A` to `F`. + // 3. Using `i >> 31` on a signed 32 bit integer extracts the sign, thanks to sign extension. + // It will be `-1` for `i < 0` and `0` for `i >= 0`. + // 4. Combining 2) and 3), shows that `(b-10)>>31` will be `0` for letters and `-1` for digits. + // 5. Looking at the case for letters, the last summand becomes `0`, and `b` is in the range 10 to 15. We want to map it to `A`(65) to `F`(70), which implies adding 55 (`'A'-10`). + // 6. Looking at the case for digits, we want to adapt the last summand so it maps `b` from the range 0 to 9 to the range `0`(48) to `9`(57). This means it needs to become -7 (`'0' - 55`). + // Now we could just multiply with 7. But since -1 is represented by all bits being 1, we can instead use `& -7` since `(0 & -7) == 0` and `(-1 & -7) == -7`. + // + // Some further considerations: + // + // * I didn't use a second loop variable to index into `c`, since measurement shows that calculating it from `i` is cheaper. + // * Using exactly `i < bytes.Length` as upper bound of the loop allows the JITter to eliminate bounds checks on `bytes[i]`, so I chose that variant. + // * Making `b` an int avoids unnecessary conversions from and to byte. + 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); + } + + // Explanation is similar to ToHexStringUpper + // constant 55 -> 87 and -7 -> -39 to compensate for the offset 32 between lowercase and uppercase letters + 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); + } + + 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; + } + + public static string ToBase64String(byte[] data) + { + if (data == null) + return null; + return Convert.ToBase64String(data); + } + + public static byte[] FromBase64String(string s) + { + if (s == null) + return null; + return Convert.FromBase64String(s); + } + } +} diff --git a/Ed25519.cs b/Ed25519.cs new file mode 100644 index 00000000..451be28c --- /dev/null +++ b/Ed25519.cs @@ -0,0 +1,147 @@ +using System; +using Chaos.NaCl.Internal.Ed25519Ref10; + +namespace Chaos.NaCl +{ + public static class Ed25519 + { + public static readonly int PublicKeySizeInBytes = 32; + public static readonly int SignatureSizeInBytes = 64; + public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2; + public static readonly int PrivateKeySeedSizeInBytes = 32; + public static readonly int SharedKeySizeInBytes = 32; + + public static bool Verify(ArraySegment signature, ArraySegment message, ArraySegment publicKey) + { + if (signature.Count != SignatureSizeInBytes) + throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count"); + return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset); + } + + public static bool Verify(byte[] signature, byte[] message, byte[] publicKey) + { + if (signature == null) + throw new ArgumentNullException("signature"); + if (message == null) + throw new ArgumentNullException("message"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (signature.Length != SignatureSizeInBytes) + throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length"); + if (publicKey.Length != PublicKeySizeInBytes) + throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length"); + return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0); + } + + public static void Sign(ArraySegment signature, ArraySegment message, ArraySegment expandedPrivateKey) + { + if (signature.Array == null) + throw new ArgumentNullException("signature.Array"); + if (signature.Count != SignatureSizeInBytes) + throw new ArgumentException("signature.Count"); + if (expandedPrivateKey.Array == null) + throw new ArgumentNullException("expandedPrivateKey.Array"); + if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes) + throw new ArgumentException("expandedPrivateKey.Count"); + if (message.Array == null) + throw new ArgumentNullException("message.Array"); + Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset); + } + + public static byte[] Sign(byte[] message, byte[] expandedPrivateKey) + { + var signature = new byte[SignatureSizeInBytes]; + Sign(new ArraySegment(signature), new ArraySegment(message), new ArraySegment(expandedPrivateKey)); + return signature; + } + + public static byte[] PublicKeyFromSeed(byte[] privateKeySeed) + { + byte[] privateKey; + byte[] publicKey; + KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed); + CryptoBytes.Wipe(privateKey); + return publicKey; + } + + public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed) + { + byte[] privateKey; + byte[] publicKey; + KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed); + CryptoBytes.Wipe(publicKey); + return privateKey; + } + + public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed) + { + if (privateKeySeed == null) + throw new ArgumentNullException("privateKeySeed"); + if (privateKeySeed.Length != PrivateKeySeedSizeInBytes) + throw new ArgumentException("privateKeySeed"); + var pk = new byte[PublicKeySizeInBytes]; + var sk = new byte[ExpandedPrivateKeySizeInBytes]; + Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0); + publicKey = pk; + expandedPrivateKey = sk; + } + + public static void KeyPairFromSeed(ArraySegment publicKey, ArraySegment expandedPrivateKey, ArraySegment privateKeySeed) + { + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (expandedPrivateKey.Array == null) + throw new ArgumentNullException("expandedPrivateKey.Array"); + if (privateKeySeed.Array == null) + throw new ArgumentNullException("privateKeySeed.Array"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException("publicKey.Count"); + if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes) + throw new ArgumentException("expandedPrivateKey.Count"); + if (privateKeySeed.Count != PrivateKeySeedSizeInBytes) + throw new ArgumentException("privateKeySeed.Count"); + Ed25519Operations.crypto_sign_keypair( + publicKey.Array, publicKey.Offset, + expandedPrivateKey.Array, expandedPrivateKey.Offset, + privateKeySeed.Array, privateKeySeed.Offset); + } + + [Obsolete("Needs more testing")] + public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey) + { + var sharedKey = new byte[SharedKeySizeInBytes]; + KeyExchange(new ArraySegment(sharedKey), new ArraySegment(publicKey), new ArraySegment(privateKey)); + return sharedKey; + } + + [Obsolete("Needs more testing")] + public static void KeyExchange(ArraySegment sharedKey, ArraySegment publicKey, ArraySegment privateKey) + { + if (sharedKey.Array == null) + throw new ArgumentNullException("sharedKey.Array"); + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey"); + if (sharedKey.Count != 32) + throw new ArgumentException("sharedKey.Count != 32"); + if (publicKey.Count != 32) + throw new ArgumentException("publicKey.Count != 32"); + if (privateKey.Count != 64) + throw new ArgumentException("privateKey.Count != 64"); + + FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX; + FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset); + FieldOperations.fe_1(out edwardsZ); + MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ); + byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc + ScalarOperations.sc_clamp(h, 0); + MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX); + CryptoBytes.Wipe(h); + FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX); + MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset); + } + } +} \ No newline at end of file diff --git a/Internal/Array16.cs b/Internal/Array16.cs new file mode 100644 index 00000000..082bf061 --- /dev/null +++ b/Internal/Array16.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace Chaos.NaCl.Internal +{ + // Array16 Salsa20 state + // Array16 SHA-512 block + internal struct Array16 + { + 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; + } +} diff --git a/Internal/Array8.cs b/Internal/Array8.cs new file mode 100644 index 00000000..f3bc50dd --- /dev/null +++ b/Internal/Array8.cs @@ -0,0 +1,18 @@ +using System; + +namespace Chaos.NaCl.Internal +{ + // Array8 Poly1305 key + // Array8 SHA-512 state/output + internal struct Array8 + { + public T x0; + public T x1; + public T x2; + public T x3; + public T x4; + public T x5; + public T x6; + public T x7; + } +} \ No newline at end of file diff --git a/Internal/ByteIntegerConverter.cs b/Internal/ByteIntegerConverter.cs new file mode 100644 index 00000000..c90aa5b1 --- /dev/null +++ b/Internal/ByteIntegerConverter.cs @@ -0,0 +1,416 @@ +using System; +using System.Collections.Generic; + +namespace Chaos.NaCl.Internal +{ + // 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 + { + #region Individual + + public static UInt32 LoadLittleEndian32(byte[] buf, int offset) + { + return + (UInt32)(buf[offset + 0]) + | (((UInt32)(buf[offset + 1])) << 8) + | (((UInt32)(buf[offset + 2])) << 16) + | (((UInt32)(buf[offset + 3])) << 24); + } + + public static void StoreLittleEndian32(byte[] buf, int offset, UInt32 value) + { + buf[offset + 0] = unchecked((byte)value); + buf[offset + 1] = unchecked((byte)(value >> 8)); + buf[offset + 2] = unchecked((byte)(value >> 16)); + buf[offset + 3] = unchecked((byte)(value >> 24)); + } + + public static UInt64 LoadBigEndian64(byte[] buf, int offset) + { + return + (UInt64)(buf[offset + 7]) + | (((UInt64)(buf[offset + 6])) << 8) + | (((UInt64)(buf[offset + 5])) << 16) + | (((UInt64)(buf[offset + 4])) << 24) + | (((UInt64)(buf[offset + 3])) << 32) + | (((UInt64)(buf[offset + 2])) << 40) + | (((UInt64)(buf[offset + 1])) << 48) + | (((UInt64)(buf[offset + 0])) << 56); + } + + public static void StoreBigEndian64(byte[] buf, int offset, UInt64 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 XorLittleEndian32(byte[] buf, int offset, UInt32 value) + { + buf[offset + 0] ^= (byte)value; + buf[offset + 1] ^= (byte)(value >> 8); + buf[offset + 2] ^= (byte)(value >> 16); + buf[offset + 3] ^= (byte)(value >> 24); + }*/ + + /*public static void XorLittleEndian32(byte[] output, int outputOffset, byte[] input, int inputOffset, UInt32 value) + { + output[outputOffset + 0] = (byte)(input[inputOffset + 0] ^ value); + output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8)); + output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16)); + output[outputOffset + 3] = (byte)(input[inputOffset + 3] ^ (value >> 24)); + }*/ + + #endregion + + #region Array8 + + public static void Array8LoadLittleEndian32(out Array8 output, byte[] input, int inputOffset) + { + output.x0 = LoadLittleEndian32(input, inputOffset + 0); + output.x1 = LoadLittleEndian32(input, inputOffset + 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 8); + output.x3 = LoadLittleEndian32(input, inputOffset + 12); + output.x4 = LoadLittleEndian32(input, inputOffset + 16); + output.x5 = LoadLittleEndian32(input, inputOffset + 20); + output.x6 = LoadLittleEndian32(input, inputOffset + 24); + output.x7 = LoadLittleEndian32(input, inputOffset + 28); + } + + /* public static void Array8LoadLittleEndian32(out Array8 output, byte[] input, int inputOffset, int inputLength) + { + #if DEBUG + if (inputLength <= 0) + throw new ArgumentException(); + #endif + int inputEnd = inputOffset + inputLength; + UInt32 highestInt; + switch (inputLength & 3) + { + case 1: + highestInt = input[inputEnd - 1]; + break; + case 2: + highestInt = (uint)( + (input[inputEnd - 1] << 8) | + (input[inputEnd - 2])); + break; + case 3: + highestInt = (uint)( + (input[inputEnd - 1] << 16) | + (input[inputEnd - 2] << 8) | + (input[inputEnd - 3])); + break; + case 0: + highestInt = (uint)( + (input[inputEnd - 1] << 24) | + (input[inputEnd - 2] << 16) | + (input[inputEnd - 3] << 8) | + (input[inputEnd - 4])); + break; + default: + throw new InvalidOperationException(); + } + switch ((inputLength - 1) >> 2) + { + case 7: + output.x7 = highestInt; + output.x6 = LoadLittleEndian32(input, inputOffset + 6 * 4); + output.x5 = LoadLittleEndian32(input, inputOffset + 5 * 4); + output.x4 = LoadLittleEndian32(input, inputOffset + 4 * 4); + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 6: + output.x7 = 0; + output.x6 = highestInt; + output.x5 = LoadLittleEndian32(input, inputOffset + 5 * 4); + output.x4 = LoadLittleEndian32(input, inputOffset + 4 * 4); + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 5: + output.x7 = 0; + output.x6 = 0; + output.x5 = highestInt; + output.x4 = LoadLittleEndian32(input, inputOffset + 4 * 4); + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 4: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = highestInt; + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 3: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = highestInt; + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 2: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = 0; + output.x2 = highestInt; + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 1: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = 0; + output.x2 = 0; + output.x1 = highestInt; + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 0: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = 0; + output.x2 = 0; + output.x1 = 0; + output.x0 = highestInt; + return; + default: + throw new InvalidOperationException(); + } + }*/ + + /*public static void Array8XorLittleEndian(byte[] output, int outputOffset, byte[] input, int inputOffset, ref Array8 keyStream, int length) + { +#if DEBUG + InternalAssert(length > 0); +#endif + int outputEnd = outputOffset + length; + UInt32 highestInt; + switch ((length - 1) >> 2) + { + case 7: + highestInt = keyStream.x7; + XorLittleEndian32(output, outputOffset + 6 * 4, input, inputOffset + 6 * 4, keyStream.x6); + XorLittleEndian32(output, outputOffset + 5 * 4, input, inputOffset + 6 * 4, keyStream.x5); + XorLittleEndian32(output, outputOffset + 4 * 4, input, inputOffset + 6 * 4, keyStream.x4); + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 6: + highestInt = keyStream.x6; + XorLittleEndian32(output, outputOffset + 5 * 4, input, inputOffset + 6 * 4, keyStream.x5); + XorLittleEndian32(output, outputOffset + 4 * 4, input, inputOffset + 6 * 4, keyStream.x4); + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 5: + highestInt = keyStream.x5; + XorLittleEndian32(output, outputOffset + 4 * 4, input, inputOffset + 6 * 4, keyStream.x4); + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 4: + highestInt = keyStream.x4; + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 3: + highestInt = keyStream.x3; + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 2: + highestInt = keyStream.x2; + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 1: + highestInt = keyStream.x1; + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 0: + highestInt = keyStream.x0; + break; + default: + throw new InvalidOperationException(); + } + switch (length & 3) + { + case 1: + output[outputEnd - 1] ^= (byte)highestInt; + break; + case 2: + output[outputEnd - 1] ^= (byte)(highestInt >> 8); + output[outputEnd - 2] ^= (byte)highestInt; + break; + case 3: + output[outputEnd - 1] ^= (byte)(highestInt >> 16); + output[outputEnd - 2] ^= (byte)(highestInt >> 8); + output[outputEnd - 3] ^= (byte)highestInt; + break; + case 0: + output[outputEnd - 1] ^= (byte)(highestInt >> 24); + output[outputEnd - 2] ^= (byte)(highestInt >> 16); + output[outputEnd - 3] ^= (byte)(highestInt >> 8); + output[outputEnd - 4] ^= (byte)highestInt; + break; + default: + throw new InvalidOperationException(); + } + }*/ + + /*public static void Array8StoreLittleEndian32(byte[] output, int outputOffset, ref Array8 input) + { + StoreLittleEndian32(output, outputOffset + 0, input.x0); + StoreLittleEndian32(output, outputOffset + 4, input.x1); + StoreLittleEndian32(output, outputOffset + 8, input.x2); + StoreLittleEndian32(output, outputOffset + 12, input.x3); + StoreLittleEndian32(output, outputOffset + 16, input.x4); + StoreLittleEndian32(output, outputOffset + 20, input.x5); + StoreLittleEndian32(output, outputOffset + 24, input.x6); + StoreLittleEndian32(output, outputOffset + 28, input.x7); + }*/ + #endregion + + public static void Array16LoadBigEndian64(out Array16 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); + } + + // ToDo: Only used in tests. Remove? + public static void Array16LoadLittleEndian32(out Array16 output, byte[] input, int inputOffset) + { + output.x0 = LoadLittleEndian32(input, inputOffset + 0); + output.x1 = LoadLittleEndian32(input, inputOffset + 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 8); + output.x3 = LoadLittleEndian32(input, inputOffset + 12); + output.x4 = LoadLittleEndian32(input, inputOffset + 16); + output.x5 = LoadLittleEndian32(input, inputOffset + 20); + output.x6 = LoadLittleEndian32(input, inputOffset + 24); + output.x7 = LoadLittleEndian32(input, inputOffset + 28); + output.x8 = LoadLittleEndian32(input, inputOffset + 32); + output.x9 = LoadLittleEndian32(input, inputOffset + 36); + output.x10 = LoadLittleEndian32(input, inputOffset + 40); + output.x11 = LoadLittleEndian32(input, inputOffset + 44); + output.x12 = LoadLittleEndian32(input, inputOffset + 48); + output.x13 = LoadLittleEndian32(input, inputOffset + 52); + output.x14 = LoadLittleEndian32(input, inputOffset + 56); + output.x15 = LoadLittleEndian32(input, inputOffset + 60); + } + + /*public static void Array16LoadLittleEndian32(out Array16 output, byte[] input, int inputOffset, int inputLength) + { + Array8 temp; + if (inputLength > 32) + { + output.x0 = LoadLittleEndian32(input, inputOffset + 0); + output.x1 = LoadLittleEndian32(input, inputOffset + 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 8); + output.x3 = LoadLittleEndian32(input, inputOffset + 12); + output.x4 = LoadLittleEndian32(input, inputOffset + 16); + output.x5 = LoadLittleEndian32(input, inputOffset + 20); + output.x6 = LoadLittleEndian32(input, inputOffset + 24); + output.x7 = LoadLittleEndian32(input, inputOffset + 28); + Array8LoadLittleEndian32(out temp, input, inputOffset + 32, inputLength - 32); + output.x8 = temp.x0; + output.x9 = temp.x1; + output.x10 = temp.x2; + output.x11 = temp.x3; + output.x12 = temp.x4; + output.x13 = temp.x5; + output.x14 = temp.x6; + output.x15 = temp.x7; + } + else + { + Array8LoadLittleEndian32(out temp, input, inputOffset, inputLength); + output.x0 = temp.x0; + output.x1 = temp.x1; + output.x2 = temp.x2; + output.x3 = temp.x3; + output.x4 = temp.x4; + output.x5 = temp.x5; + output.x6 = temp.x6; + output.x7 = temp.x7; + output.x8 = 0; + output.x9 = 0; + output.x10 = 0; + output.x11 = 0; + output.x12 = 0; + output.x13 = 0; + output.x14 = 0; + output.x15 = 0; + } + }*/ + + public static void Array16StoreLittleEndian32(byte[] output, int outputOffset, ref Array16 input) + { + StoreLittleEndian32(output, outputOffset + 0, input.x0); + StoreLittleEndian32(output, outputOffset + 4, input.x1); + StoreLittleEndian32(output, outputOffset + 8, input.x2); + StoreLittleEndian32(output, outputOffset + 12, input.x3); + StoreLittleEndian32(output, outputOffset + 16, input.x4); + StoreLittleEndian32(output, outputOffset + 20, input.x5); + StoreLittleEndian32(output, outputOffset + 24, input.x6); + StoreLittleEndian32(output, outputOffset + 28, input.x7); + StoreLittleEndian32(output, outputOffset + 32, input.x8); + StoreLittleEndian32(output, outputOffset + 36, input.x9); + StoreLittleEndian32(output, outputOffset + 40, input.x10); + StoreLittleEndian32(output, outputOffset + 44, input.x11); + StoreLittleEndian32(output, outputOffset + 48, input.x12); + StoreLittleEndian32(output, outputOffset + 52, input.x13); + StoreLittleEndian32(output, outputOffset + 56, input.x14); + StoreLittleEndian32(output, outputOffset + 60, input.x15); + } + } +} diff --git a/Internal/Ed25519Ref10/FieldElement.cs b/Internal/Ed25519Ref10/FieldElement.cs new file mode 100644 index 00000000..5a5c785b --- /dev/null +++ b/Internal/Ed25519Ref10/FieldElement.cs @@ -0,0 +1,36 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal struct FieldElement + { + internal int x0; + internal int x1; + internal int x2; + internal int x3; + internal int x4; + internal int x5; + internal int x6; + internal int x7; + internal int x8; + internal int x9; + + //public static readonly FieldElement Zero = new FieldElement(); + //public static readonly FieldElement One = new FieldElement() { x0 = 1 }; + + internal FieldElement(params int[] elements) + { + InternalAssert.Assert(elements.Length == 10, "elements.Length != 10"); + 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]; + } + } +} diff --git a/Internal/Ed25519Ref10/GroupElement.cs b/Internal/Ed25519Ref10/GroupElement.cs new file mode 100644 index 00000000..abeaca86 --- /dev/null +++ b/Internal/Ed25519Ref10/GroupElement.cs @@ -0,0 +1,63 @@ +using System; + +namespace Chaos.NaCl.Internal.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; + } ; +} diff --git a/Internal/Ed25519Ref10/base.cs b/Internal/Ed25519Ref10/base.cs new file mode 100644 index 00000000..5627e9c7 --- /dev/null +++ b/Internal/Ed25519Ref10/base.cs @@ -0,0 +1,1357 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + /* base[i][j] = (j+1)*256^i*B */ + //32*8 + internal static GroupElementPreComp[][] Base = new GroupElementPreComp[][] + { + new[]{ + 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( -12815894,-12976347,-21581243,11784320,-25355658,-2750717,-11717903,-3814571,-358445,-10211303 ), + new FieldElement( -21703237,6903825,27185491,6451973,-29577724,-9554005,-15616551,11189268,-26829678,-5319081 ), + new FieldElement( 26966642,11152617,32442495,15396054,14353839,-12752335,-3128826,-9541118,-15472047,-4166697 ) + ), + 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( -17036878,13921892,10945806,-6033431,27105052,-16084379,-28926210,15006023,3284568,-6276540 ), + new FieldElement( 23599295,-8306047,-11193664,-7687416,13236774,10506355,7464579,9656445,13059162,10374397 ), + new FieldElement( 7798556,16710257,3033922,2874086,28997861,2835604,32406664,-3839045,-641708,-101325 ) + ), + 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( -15371964,-12862754,32573250,4720197,-26436522,5875511,-19188627,-15224819,-9818940,-12085777 ), + new FieldElement( -8549212,109983,15149363,2178705,22900618,4543417,3044240,-15689887,1762328,14866737 ), + new FieldElement( -18199695,-15951423,-10473290,1707278,-17185920,3916101,-28236412,3959421,27914454,4383652 ) + ), + 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( 14499471,-2729599,-33191113,-4254652,28494862,14271267,30290735,10876454,-33154098,2381726 ), + new FieldElement( -7195431,-2655363,-14730155,462251,-27724326,3941372,-6236617,3696005,-32300832,15351955 ), + new FieldElement( 27431194,8222322,16448760,-3907995,-18707002,11938355,-32961401,-2970515,29551813,10109425 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -13657040,-13155431,-31283750,11777098,21447386,6519384,-2378284,-1627556,10092783,-4764171 ), + new FieldElement( 27939166,14210322,4677035,16277044,-22964462,-12398139,-32508754,12005538,-17810127,12803510 ), + new FieldElement( 17228999,-15661624,-1233527,300140,-1224870,-11714777,30364213,-9038194,18016357,4397660 ) + ), + new GroupElementPreComp( + new FieldElement( -10958843,-7690207,4776341,-14954238,27850028,-15602212,-26619106,14544525,-17477504,982639 ), + new FieldElement( 29253598,15796703,-2863982,-9908884,10057023,3163536,7332899,-4120128,-21047696,9934963 ), + new FieldElement( 5793303,16271923,-24131614,-10116404,29188560,1206517,-14747930,4559895,-30123922,-10897950 ) + ), + new GroupElementPreComp( + new FieldElement( -27643952,-11493006,16282657,-11036493,28414021,-15012264,24191034,4541697,-13338309,5500568 ), + new FieldElement( 12650548,-1497113,9052871,11355358,-17680037,-8400164,-17430592,12264343,10874051,13524335 ), + new FieldElement( 25556948,-3045990,714651,2510400,23394682,-10415330,33119038,5080568,-22528059,5376628 ) + ), + new GroupElementPreComp( + new FieldElement( -26088264,-4011052,-17013699,-3537628,-6726793,1920897,-22321305,-9447443,4535768,1569007 ), + new FieldElement( -2255422,14606630,-21692440,-8039818,28430649,8775819,-30494562,3044290,31848280,12543772 ), + new FieldElement( -22028579,2943893,-31857513,6777306,13784462,-4292203,-27377195,-2062731,7718482,14474653 ) + ), + new GroupElementPreComp( + new FieldElement( 2385315,2454213,-22631320,46603,-4437935,-15680415,656965,-7236665,24316168,-5253567 ), + new FieldElement( 13741529,10911568,-33233417,-8603737,-20177830,-1033297,33040651,-13424532,-20729456,8321686 ), + new FieldElement( 21060490,-2212744,15712757,-4336099,1639040,10656336,23845965,-11874838,-9984458,608372 ) + ), + new GroupElementPreComp( + new FieldElement( -13672732,-15087586,-10889693,-7557059,-6036909,11305547,1123968,-6780577,27229399,23887 ), + new FieldElement( -23244140,-294205,-11744728,14712571,-29465699,-2029617,12797024,-6440308,-1633405,16678954 ), + new FieldElement( -29500620,4770662,-16054387,14001338,7830047,9564805,-1508144,-4795045,-17169265,4904953 ) + ), + new GroupElementPreComp( + new FieldElement( 24059557,14617003,19037157,-15039908,19766093,-14906429,5169211,16191880,2128236,-4326833 ), + new FieldElement( -16981152,4124966,-8540610,-10653797,30336522,-14105247,-29806336,916033,-6882542,-2986532 ), + new FieldElement( -22630907,12419372,-7134229,-7473371,-16478904,16739175,285431,2763829,15736322,4143876 ) + ), + new GroupElementPreComp( + new FieldElement( 2379352,11839345,-4110402,-5988665,11274298,794957,212801,-14594663,23527084,-16458268 ), + new FieldElement( 33431127,-11130478,-17838966,-15626900,8909499,8376530,-32625340,4087881,-15188911,-14416214 ), + new FieldElement( 1767683,7197987,-13205226,-2022635,-13091350,448826,5799055,4357868,-4774191,-16323038 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 6721966,13833823,-23523388,-1551314,26354293,-11863321,23365147,-3949732,7390890,2759800 ), + new FieldElement( 4409041,2052381,23373853,10530217,7676779,-12885954,21302353,-4264057,1244380,-12919645 ), + new FieldElement( -4421239,7169619,4982368,-2957590,30256825,-2777540,14086413,9208236,15886429,16489664 ) + ), + new GroupElementPreComp( + new FieldElement( 1996075,10375649,14346367,13311202,-6874135,-16438411,-13693198,398369,-30606455,-712933 ), + new FieldElement( -25307465,9795880,-2777414,14878809,-33531835,14780363,13348553,12076947,-30836462,5113182 ), + new FieldElement( -17770784,11797796,31950843,13929123,-25888302,12288344,-30341101,-7336386,13847711,5387222 ) + ), + new GroupElementPreComp( + new FieldElement( -18582163,-3416217,17824843,-2340966,22744343,-10442611,8763061,3617786,-19600662,10370991 ), + new FieldElement( 20246567,-14369378,22358229,-543712,18507283,-10413996,14554437,-8746092,32232924,16763880 ), + new FieldElement( 9648505,10094563,26416693,14745928,-30374318,-6472621,11094161,15689506,3140038,-16510092 ) + ), + new GroupElementPreComp( + new FieldElement( -16160072,5472695,31895588,4744994,8823515,10365685,-27224800,9448613,-28774454,366295 ), + new FieldElement( 19153450,11523972,-11096490,-6503142,-24647631,5420647,28344573,8041113,719605,11671788 ), + new FieldElement( 8678025,2694440,-6808014,2517372,4964326,11152271,-15432916,-15266516,27000813,-10195553 ) + ), + new GroupElementPreComp( + new FieldElement( -15157904,7134312,8639287,-2814877,-7235688,10421742,564065,5336097,6750977,-14521026 ), + new FieldElement( 11836410,-3979488,26297894,16080799,23455045,15735944,1695823,-8819122,8169720,16220347 ), + new FieldElement( -18115838,8653647,17578566,-6092619,-8025777,-16012763,-11144307,-2627664,-5990708,-14166033 ) + ), + new GroupElementPreComp( + new FieldElement( -23308498,-10968312,15213228,-10081214,-30853605,-11050004,27884329,2847284,2655861,1738395 ), + new FieldElement( -27537433,-14253021,-25336301,-8002780,-9370762,8129821,21651608,-3239336,-19087449,-11005278 ), + new FieldElement( 1533110,3437855,23735889,459276,29970501,11335377,26030092,5821408,10478196,8544890 ) + ), + new GroupElementPreComp( + new FieldElement( 32173121,-16129311,24896207,3921497,22579056,-3410854,19270449,12217473,17789017,-3395995 ), + new FieldElement( -30552961,-2228401,-15578829,-10147201,13243889,517024,15479401,-3853233,30460520,1052596 ), + new FieldElement( -11614875,13323618,32618793,8175907,-15230173,12596687,27491595,-4612359,3179268,-9478891 ) + ), + new GroupElementPreComp( + new FieldElement( 31947069,-14366651,-4640583,-15339921,-15125977,-6039709,-14756777,-16411740,19072640,-9511060 ), + new FieldElement( 11685058,11822410,3158003,-13952594,33402194,-4165066,5977896,-5215017,473099,5040608 ), + new FieldElement( -20290863,8198642,-27410132,11602123,1290375,-2799760,28326862,1721092,-19558642,-3131606 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 7881532,10687937,7578723,7738378,-18951012,-2553952,21820786,8076149,-27868496,11538389 ), + new FieldElement( -19935666,3899861,18283497,-6801568,-15728660,-11249211,8754525,7446702,-5676054,5797016 ), + new FieldElement( -11295600,-3793569,-15782110,-7964573,12708869,-8456199,2014099,-9050574,-2369172,-5877341 ) + ), + new GroupElementPreComp( + new FieldElement( -22472376,-11568741,-27682020,1146375,18956691,16640559,1192730,-3714199,15123619,10811505 ), + new FieldElement( 14352098,-3419715,-18942044,10822655,32750596,4699007,-70363,15776356,-28886779,-11974553 ), + new FieldElement( -28241164,-8072475,-4978962,-5315317,29416931,1847569,-20654173,-16484855,4714547,-9600655 ) + ), + new GroupElementPreComp( + new FieldElement( 15200332,8368572,19679101,15970074,-31872674,1959451,24611599,-4543832,-11745876,12340220 ), + new FieldElement( 12876937,-10480056,33134381,6590940,-6307776,14872440,9613953,8241152,15370987,9608631 ), + new FieldElement( -4143277,-12014408,8446281,-391603,4407738,13629032,-7724868,15866074,-28210621,-8814099 ) + ), + new GroupElementPreComp( + new FieldElement( 26660628,-15677655,8393734,358047,-7401291,992988,-23904233,858697,20571223,8420556 ), + new FieldElement( 14620715,13067227,-15447274,8264467,14106269,15080814,33531827,12516406,-21574435,-12476749 ), + new FieldElement( 236881,10476226,57258,-14677024,6472998,2466984,17258519,7256740,8791136,15069930 ) + ), + new GroupElementPreComp( + new FieldElement( 1276410,-9371918,22949635,-16322807,-23493039,-5702186,14711875,4874229,-30663140,-2331391 ), + new FieldElement( 5855666,4990204,-13711848,7294284,-7804282,1924647,-1423175,-7912378,-33069337,9234253 ), + new FieldElement( 20590503,-9018988,31529744,-7352666,-2706834,10650548,31559055,-11609587,18979186,13396066 ) + ), + new GroupElementPreComp( + new FieldElement( 24474287,4968103,22267082,4407354,24063882,-8325180,-18816887,13594782,33514650,7021958 ), + new FieldElement( -11566906,-6565505,-21365085,15928892,-26158305,4315421,-25948728,-3916677,-21480480,12868082 ), + new FieldElement( -28635013,13504661,19988037,-2132761,21078225,6443208,-21446107,2244500,-12455797,-8089383 ) + ), + new GroupElementPreComp( + new FieldElement( -30595528,13793479,-5852820,319136,-25723172,-6263899,33086546,8957937,-15233648,5540521 ), + new FieldElement( -11630176,-11503902,-8119500,-7643073,2620056,1022908,-23710744,-1568984,-16128528,-14962807 ), + new FieldElement( 23152971,775386,27395463,14006635,-9701118,4649512,1689819,892185,-11513277,-15205948 ) + ), + new GroupElementPreComp( + new FieldElement( 9770129,9586738,26496094,4324120,1556511,-3550024,27453819,4763127,-19179614,5867134 ), + new FieldElement( -32765025,1927590,31726409,-4753295,23962434,-16019500,27846559,5931263,-29749703,-16108455 ), + new FieldElement( 27461885,-2977536,22380810,1815854,-23033753,-3031938,7283490,-15148073,-19526700,7734629 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -8010264,-9590817,-11120403,6196038,29344158,-13430885,7585295,-3176626,18549497,15302069 ), + new FieldElement( -32658337,-6171222,-7672793,-11051681,6258878,13504381,10458790,-6418461,-8872242,8424746 ), + new FieldElement( 24687205,8613276,-30667046,-3233545,1863892,-1830544,19206234,7134917,-11284482,-828919 ) + ), + new GroupElementPreComp( + new FieldElement( 11334899,-9218022,8025293,12707519,17523892,-10476071,10243738,-14685461,-5066034,16498837 ), + new FieldElement( 8911542,6887158,-9584260,-6958590,11145641,-9543680,17303925,-14124238,6536641,10543906 ), + new FieldElement( -28946384,15479763,-17466835,568876,-1497683,11223454,-2669190,-16625574,-27235709,8876771 ) + ), + new GroupElementPreComp( + new FieldElement( -25742899,-12566864,-15649966,-846607,-33026686,-796288,-33481822,15824474,-604426,-9039817 ), + new FieldElement( 10330056,70051,7957388,-9002667,9764902,15609756,27698697,-4890037,1657394,3084098 ), + new FieldElement( 10477963,-7470260,12119566,-13250805,29016247,-5365589,31280319,14396151,-30233575,15272409 ) + ), + new GroupElementPreComp( + new FieldElement( -12288309,3169463,28813183,16658753,25116432,-5630466,-25173957,-12636138,-25014757,1950504 ), + new FieldElement( -26180358,9489187,11053416,-14746161,-31053720,5825630,-8384306,-8767532,15341279,8373727 ), + new FieldElement( 28685821,7759505,-14378516,-12002860,-31971820,4079242,298136,-10232602,-2878207,15190420 ) + ), + new GroupElementPreComp( + new FieldElement( -32932876,13806336,-14337485,-15794431,-24004620,10940928,8669718,2742393,-26033313,-6875003 ), + new FieldElement( -1580388,-11729417,-25979658,-11445023,-17411874,-10912854,9291594,-16247779,-12154742,6048605 ), + new FieldElement( -30305315,14843444,1539301,11864366,20201677,1900163,13934231,5128323,11213262,9168384 ) + ), + new GroupElementPreComp( + new FieldElement( -26280513,11007847,19408960,-940758,-18592965,-4328580,-5088060,-11105150,20470157,-16398701 ), + new FieldElement( -23136053,9282192,14855179,-15390078,-7362815,-14408560,-22783952,14461608,14042978,5230683 ), + new FieldElement( 29969567,-2741594,-16711867,-8552442,9175486,-2468974,21556951,3506042,-5933891,-12449708 ) + ), + new GroupElementPreComp( + new FieldElement( -3144746,8744661,19704003,4581278,-20430686,6830683,-21284170,8971513,-28539189,15326563 ), + new FieldElement( -19464629,10110288,-17262528,-3503892,-23500387,1355669,-15523050,15300988,-20514118,9168260 ), + new FieldElement( -5353335,4488613,-23803248,16314347,7780487,-15638939,-28948358,9601605,33087103,-9011387 ) + ), + new GroupElementPreComp( + new FieldElement( -19443170,-15512900,-20797467,-12445323,-29824447,10229461,-27444329,-15000531,-5996870,15664672 ), + new FieldElement( 23294591,-16632613,-22650781,-8470978,27844204,11461195,13099750,-2460356,18151676,13417686 ), + new FieldElement( -24722913,-4176517,-31150679,5988919,-26858785,6685065,1661597,-12551441,15271676,-15452665 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 11433042,-13228665,8239631,-5279517,-1985436,-725718,-18698764,2167544,-6921301,-13440182 ), + new FieldElement( -31436171,15575146,30436815,12192228,-22463353,9395379,-9917708,-8638997,12215110,12028277 ), + new FieldElement( 14098400,6555944,23007258,5757252,-15427832,-12950502,30123440,4617780,-16900089,-655628 ) + ), + new GroupElementPreComp( + new FieldElement( -4026201,-15240835,11893168,13718664,-14809462,1847385,-15819999,10154009,23973261,-12684474 ), + new FieldElement( -26531820,-3695990,-1908898,2534301,-31870557,-16550355,18341390,-11419951,32013174,-10103539 ), + new FieldElement( -25479301,10876443,-11771086,-14625140,-12369567,1838104,21911214,6354752,4425632,-837822 ) + ), + new GroupElementPreComp( + new FieldElement( -10433389,-14612966,22229858,-3091047,-13191166,776729,-17415375,-12020462,4725005,14044970 ), + new FieldElement( 19268650,-7304421,1555349,8692754,-21474059,-9910664,6347390,-1411784,-19522291,-16109756 ), + new FieldElement( -24864089,12986008,-10898878,-5558584,-11312371,-148526,19541418,8180106,9282262,10282508 ) + ), + new GroupElementPreComp( + new FieldElement( -26205082,4428547,-8661196,-13194263,4098402,-14165257,15522535,8372215,5542595,-10702683 ), + new FieldElement( -10562541,14895633,26814552,-16673850,-17480754,-2489360,-2781891,6993761,-18093885,10114655 ), + new FieldElement( -20107055,-929418,31422704,10427861,-7110749,6150669,-29091755,-11529146,25953725,-106158 ) + ), + new GroupElementPreComp( + new FieldElement( -4234397,-8039292,-9119125,3046000,2101609,-12607294,19390020,6094296,-3315279,12831125 ), + new FieldElement( -15998678,7578152,5310217,14408357,-33548620,-224739,31575954,6326196,7381791,-2421839 ), + new FieldElement( -20902779,3296811,24736065,-16328389,18374254,7318640,6295303,8082724,-15362489,12339664 ) + ), + new GroupElementPreComp( + new FieldElement( 27724736,2291157,6088201,-14184798,1792727,5857634,13848414,15768922,25091167,14856294 ), + new FieldElement( -18866652,8331043,24373479,8541013,-701998,-9269457,12927300,-12695493,-22182473,-9012899 ), + new FieldElement( -11423429,-5421590,11632845,3405020,30536730,-11674039,-27260765,13866390,30146206,9142070 ) + ), + new GroupElementPreComp( + new FieldElement( 3924129,-15307516,-13817122,-10054960,12291820,-668366,-27702774,9326384,-8237858,4171294 ), + new FieldElement( -15921940,16037937,6713787,16606682,-21612135,2790944,26396185,3731949,345228,-5462949 ), + new FieldElement( -21327538,13448259,25284571,1143661,20614966,-8849387,2031539,-12391231,-16253183,-13582083 ) + ), + new GroupElementPreComp( + new FieldElement( 31016211,-16722429,26371392,-14451233,-5027349,14854137,17477601,3842657,28012650,-16405420 ), + new FieldElement( -5075835,9368966,-8562079,-4600902,-15249953,6970560,-9189873,16292057,-8867157,3507940 ), + new FieldElement( 29439664,3537914,23333589,6997794,-17555561,-11018068,-15209202,-15051267,-9164929,6580396 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -12185861,-7679788,16438269,10826160,-8696817,-6235611,17860444,-9273846,-2095802,9304567 ), + new FieldElement( 20714564,-4336911,29088195,7406487,11426967,-5095705,14792667,-14608617,5289421,-477127 ), + new FieldElement( -16665533,-10650790,-6160345,-13305760,9192020,-1802462,17271490,12349094,26939669,-3752294 ) + ), + new GroupElementPreComp( + new FieldElement( -12889898,9373458,31595848,16374215,21471720,13221525,-27283495,-12348559,-3698806,117887 ), + new FieldElement( 22263325,-6560050,3984570,-11174646,-15114008,-566785,28311253,5358056,-23319780,541964 ), + new FieldElement( 16259219,3261970,2309254,-15534474,-16885711,-4581916,24134070,-16705829,-13337066,-13552195 ) + ), + new GroupElementPreComp( + new FieldElement( 9378160,-13140186,-22845982,-12745264,28198281,-7244098,-2399684,-717351,690426,14876244 ), + new FieldElement( 24977353,-314384,-8223969,-13465086,28432343,-1176353,-13068804,-12297348,-22380984,6618999 ), + new FieldElement( -1538174,11685646,12944378,13682314,-24389511,-14413193,8044829,-13817328,32239829,-5652762 ) + ), + new GroupElementPreComp( + new FieldElement( -18603066,4762990,-926250,8885304,-28412480,-3187315,9781647,-10350059,32779359,5095274 ), + new FieldElement( -33008130,-5214506,-32264887,-3685216,9460461,-9327423,-24601656,14506724,21639561,-2630236 ), + new FieldElement( -16400943,-13112215,25239338,15531969,3987758,-4499318,-1289502,-6863535,17874574,558605 ) + ), + new GroupElementPreComp( + new FieldElement( -13600129,10240081,9171883,16131053,-20869254,9599700,33499487,5080151,2085892,5119761 ), + new FieldElement( -22205145,-2519528,-16381601,414691,-25019550,2170430,30634760,-8363614,-31999993,-5759884 ), + new FieldElement( -6845704,15791202,8550074,-1312654,29928809,-12092256,27534430,-7192145,-22351378,12961482 ) + ), + new GroupElementPreComp( + new FieldElement( -24492060,-9570771,10368194,11582341,-23397293,-2245287,16533930,8206996,-30194652,-5159638 ), + new FieldElement( -11121496,-3382234,2307366,6362031,-135455,8868177,-16835630,7031275,7589640,8945490 ), + new FieldElement( -32152748,8917967,6661220,-11677616,-1192060,-15793393,7251489,-11182180,24099109,-14456170 ) + ), + new GroupElementPreComp( + new FieldElement( 5019558,-7907470,4244127,-14714356,-26933272,6453165,-19118182,-13289025,-6231896,-10280736 ), + new FieldElement( 10853594,10721687,26480089,5861829,-22995819,1972175,-1866647,-10557898,-3363451,-6441124 ), + new FieldElement( -17002408,5906790,221599,-6563147,7828208,-13248918,24362661,-2008168,-13866408,7421392 ) + ), + new GroupElementPreComp( + new FieldElement( 8139927,-6546497,32257646,-5890546,30375719,1886181,-21175108,15441252,28826358,-4123029 ), + new FieldElement( 6267086,9695052,7709135,-16603597,-32869068,-1886135,14795160,-7840124,13746021,-1742048 ), + new FieldElement( 28584902,7787108,-6732942,-15050729,22846041,-7571236,-3181936,-363524,4771362,-8419958 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 24949256,6376279,-27466481,-8174608,-18646154,-9930606,33543569,-12141695,3569627,11342593 ), + new FieldElement( 26514989,4740088,27912651,3697550,19331575,-11472339,6809886,4608608,7325975,-14801071 ), + new FieldElement( -11618399,-14554430,-24321212,7655128,-1369274,5214312,-27400540,10258390,-17646694,-8186692 ) + ), + new GroupElementPreComp( + new FieldElement( 11431204,15823007,26570245,14329124,18029990,4796082,-31446179,15580664,9280358,-3973687 ), + new FieldElement( -160783,-10326257,-22855316,-4304997,-20861367,-13621002,-32810901,-11181622,-15545091,4387441 ), + new FieldElement( -20799378,12194512,3937617,-5805892,-27154820,9340370,-24513992,8548137,20617071,-7482001 ) + ), + new GroupElementPreComp( + new FieldElement( -938825,-3930586,-8714311,16124718,24603125,-6225393,-13775352,-11875822,24345683,10325460 ), + new FieldElement( -19855277,-1568885,-22202708,8714034,14007766,6928528,16318175,-1010689,4766743,3552007 ), + new FieldElement( -21751364,-16730916,1351763,-803421,-4009670,3950935,3217514,14481909,10988822,-3994762 ) + ), + new GroupElementPreComp( + new FieldElement( 15564307,-14311570,3101243,5684148,30446780,-8051356,12677127,-6505343,-8295852,13296005 ), + new FieldElement( -9442290,6624296,-30298964,-11913677,-4670981,-2057379,31521204,9614054,-30000824,12074674 ), + new FieldElement( 4771191,-135239,14290749,-13089852,27992298,14998318,-1413936,-1556716,29832613,-16391035 ) + ), + new GroupElementPreComp( + new FieldElement( 7064884,-7541174,-19161962,-5067537,-18891269,-2912736,25825242,5293297,-27122660,13101590 ), + new FieldElement( -2298563,2439670,-7466610,1719965,-27267541,-16328445,32512469,-5317593,-30356070,-4190957 ), + new FieldElement( -30006540,10162316,-33180176,3981723,-16482138,-13070044,14413974,9515896,19568978,9628812 ) + ), + new GroupElementPreComp( + new FieldElement( 33053803,199357,15894591,1583059,27380243,-4580435,-17838894,-6106839,-6291786,3437740 ), + new FieldElement( -18978877,3884493,19469877,12726490,15913552,13614290,-22961733,70104,7463304,4176122 ), + new FieldElement( -27124001,10659917,11482427,-16070381,12771467,-6635117,-32719404,-5322751,24216882,5944158 ) + ), + new GroupElementPreComp( + new FieldElement( 8894125,7450974,-2664149,-9765752,-28080517,-12389115,19345746,14680796,11632993,5847885 ), + new FieldElement( 26942781,-2315317,9129564,-4906607,26024105,11769399,-11518837,6367194,-9727230,4782140 ), + new FieldElement( 19916461,-4828410,-22910704,-11414391,25606324,-5972441,33253853,8220911,6358847,-1873857 ) + ), + new GroupElementPreComp( + new FieldElement( 801428,-2081702,16569428,11065167,29875704,96627,7908388,-4480480,-13538503,1387155 ), + new FieldElement( 19646058,5720633,-11416706,12814209,11607948,12749789,14147075,15156355,-21866831,11835260 ), + new FieldElement( 19299512,1155910,28703737,14890794,2925026,7269399,26121523,15467869,-26560550,5052483 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -3017432,10058206,1980837,3964243,22160966,12322533,-6431123,-12618185,12228557,-7003677 ), + new FieldElement( 32944382,14922211,-22844894,5188528,21913450,-8719943,4001465,13238564,-6114803,8653815 ), + new FieldElement( 22865569,-4652735,27603668,-12545395,14348958,8234005,24808405,5719875,28483275,2841751 ) + ), + new GroupElementPreComp( + new FieldElement( -16420968,-1113305,-327719,-12107856,21886282,-15552774,-1887966,-315658,19932058,-12739203 ), + new FieldElement( -11656086,10087521,-8864888,-5536143,-19278573,-3055912,3999228,13239134,-4777469,-13910208 ), + new FieldElement( 1382174,-11694719,17266790,9194690,-13324356,9720081,20403944,11284705,-14013818,3093230 ) + ), + new GroupElementPreComp( + new FieldElement( 16650921,-11037932,-1064178,1570629,-8329746,7352753,-302424,16271225,-24049421,-6691850 ), + new FieldElement( -21911077,-5927941,-4611316,-5560156,-31744103,-10785293,24123614,15193618,-21652117,-16739389 ), + new FieldElement( -9935934,-4289447,-25279823,4372842,2087473,10399484,31870908,14690798,17361620,11864968 ) + ), + new GroupElementPreComp( + new FieldElement( -11307610,6210372,13206574,5806320,-29017692,-13967200,-12331205,-7486601,-25578460,-16240689 ), + new FieldElement( 14668462,-12270235,26039039,15305210,25515617,4542480,10453892,6577524,9145645,-6443880 ), + new FieldElement( 5974874,3053895,-9433049,-10385191,-31865124,3225009,-7972642,3936128,-5652273,-3050304 ) + ), + new GroupElementPreComp( + new FieldElement( 30625386,-4729400,-25555961,-12792866,-20484575,7695099,17097188,-16303496,-27999779,1803632 ), + new FieldElement( -3553091,9865099,-5228566,4272701,-5673832,-16689700,14911344,12196514,-21405489,7047412 ), + new FieldElement( 20093277,9920966,-11138194,-5343857,13161587,12044805,-32856851,4124601,-32343828,-10257566 ) + ), + new GroupElementPreComp( + new FieldElement( -20788824,14084654,-13531713,7842147,19119038,-13822605,4752377,-8714640,-21679658,2288038 ), + new FieldElement( -26819236,-3283715,29965059,3039786,-14473765,2540457,29457502,14625692,-24819617,12570232 ), + new FieldElement( -1063558,-11551823,16920318,12494842,1278292,-5869109,-21159943,-3498680,-11974704,4724943 ) + ), + new GroupElementPreComp( + new FieldElement( 17960970,-11775534,-4140968,-9702530,-8876562,-1410617,-12907383,-8659932,-29576300,1903856 ), + new FieldElement( 23134274,-14279132,-10681997,-1611936,20684485,15770816,-12989750,3190296,26955097,14109738 ), + new FieldElement( 15308788,5320727,-30113809,-14318877,22902008,7767164,29425325,-11277562,31960942,11934971 ) + ), + new GroupElementPreComp( + new FieldElement( -27395711,8435796,4109644,12222639,-24627868,14818669,20638173,4875028,10491392,1379718 ), + new FieldElement( -13159415,9197841,3875503,-8936108,-1383712,-5879801,33518459,16176658,21432314,12180697 ), + new FieldElement( -11787308,11500838,13787581,-13832590,-22430679,10140205,1465425,12689540,-10301319,-13872883 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 5414091,-15386041,-21007664,9643570,12834970,1186149,-2622916,-1342231,26128231,6032912 ), + new FieldElement( -26337395,-13766162,32496025,-13653919,17847801,-12669156,3604025,8316894,-25875034,-10437358 ), + new FieldElement( 3296484,6223048,24680646,-12246460,-23052020,5903205,-8862297,-4639164,12376617,3188849 ) + ), + new GroupElementPreComp( + new FieldElement( 29190488,-14659046,27549113,-1183516,3520066,-10697301,32049515,-7309113,-16109234,-9852307 ), + new FieldElement( -14744486,-9309156,735818,-598978,-20407687,-5057904,25246078,-15795669,18640741,-960977 ), + new FieldElement( -6928835,-16430795,10361374,5642961,4910474,12345252,-31638386,-494430,10530747,1053335 ) + ), + new GroupElementPreComp( + new FieldElement( -29265967,-14186805,-13538216,-12117373,-19457059,-10655384,-31462369,-2948985,24018831,15026644 ), + new FieldElement( -22592535,-3145277,-2289276,5953843,-13440189,9425631,25310643,13003497,-2314791,-15145616 ), + new FieldElement( -27419985,-603321,-8043984,-1669117,-26092265,13987819,-27297622,187899,-23166419,-2531735 ) + ), + new GroupElementPreComp( + new FieldElement( -21744398,-13810475,1844840,5021428,-10434399,-15911473,9716667,16266922,-5070217,726099 ), + new FieldElement( 29370922,-6053998,7334071,-15342259,9385287,2247707,-13661962,-4839461,30007388,-15823341 ), + new FieldElement( -936379,16086691,23751945,-543318,-1167538,-5189036,9137109,730663,9835848,4555336 ) + ), + new GroupElementPreComp( + new FieldElement( -23376435,1410446,-22253753,-12899614,30867635,15826977,17693930,544696,-11985298,12422646 ), + new FieldElement( 31117226,-12215734,-13502838,6561947,-9876867,-12757670,-5118685,-4096706,29120153,13924425 ), + new FieldElement( -17400879,-14233209,19675799,-2734756,-11006962,-5858820,-9383939,-11317700,7240931,-237388 ) + ), + new GroupElementPreComp( + new FieldElement( -31361739,-11346780,-15007447,-5856218,-22453340,-12152771,1222336,4389483,3293637,-15551743 ), + new FieldElement( -16684801,-14444245,11038544,11054958,-13801175,-3338533,-24319580,7733547,12796905,-6335822 ), + new FieldElement( -8759414,-10817836,-25418864,10783769,-30615557,-9746811,-28253339,3647836,3222231,-11160462 ) + ), + new GroupElementPreComp( + new FieldElement( 18606113,1693100,-25448386,-15170272,4112353,10045021,23603893,-2048234,-7550776,2484985 ), + new FieldElement( 9255317,-3131197,-12156162,-1004256,13098013,-9214866,16377220,-2102812,-19802075,-3034702 ), + new FieldElement( -22729289,7496160,-5742199,11329249,19991973,-3347502,-31718148,9936966,-30097688,-10618797 ) + ), + new GroupElementPreComp( + new FieldElement( 21878590,-5001297,4338336,13643897,-3036865,13160960,19708896,5415497,-7360503,-4109293 ), + new FieldElement( 27736861,10103576,12500508,8502413,-3413016,-9633558,10436918,-1550276,-23659143,-8132100 ), + new FieldElement( 19492550,-12104365,-29681976,-852630,-3208171,12403437,30066266,8367329,13243957,8709688 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 12015105,2801261,28198131,10151021,24818120,-4743133,-11194191,-5645734,5150968,7274186 ), + new FieldElement( 2831366,-12492146,1478975,6122054,23825128,-12733586,31097299,6083058,31021603,-9793610 ), + new FieldElement( -2529932,-2229646,445613,10720828,-13849527,-11505937,-23507731,16354465,15067285,-14147707 ) + ), + new GroupElementPreComp( + new FieldElement( 7840942,14037873,-33364863,15934016,-728213,-3642706,21403988,1057586,-19379462,-12403220 ), + new FieldElement( 915865,-16469274,15608285,-8789130,-24357026,6060030,-17371319,8410997,-7220461,16527025 ), + new FieldElement( 32922597,-556987,20336074,-16184568,10903705,-5384487,16957574,52992,23834301,6588044 ) + ), + new GroupElementPreComp( + new FieldElement( 32752030,11232950,3381995,-8714866,22652988,-10744103,17159699,16689107,-20314580,-1305992 ), + new FieldElement( -4689649,9166776,-25710296,-10847306,11576752,12733943,7924251,-2752281,1976123,-7249027 ), + new FieldElement( 21251222,16309901,-2983015,-6783122,30810597,12967303,156041,-3371252,12331345,-8237197 ) + ), + new GroupElementPreComp( + new FieldElement( 8651614,-4477032,-16085636,-4996994,13002507,2950805,29054427,-5106970,10008136,-4667901 ), + new FieldElement( 31486080,15114593,-14261250,12951354,14369431,-7387845,16347321,-13662089,8684155,-10532952 ), + new FieldElement( 19443825,11385320,24468943,-9659068,-23919258,2187569,-26263207,-6086921,31316348,14219878 ) + ), + new GroupElementPreComp( + new FieldElement( -28594490,1193785,32245219,11392485,31092169,15722801,27146014,6992409,29126555,9207390 ), + new FieldElement( 32382935,1110093,18477781,11028262,-27411763,-7548111,-4980517,10843782,-7957600,-14435730 ), + new FieldElement( 2814918,7836403,27519878,-7868156,-20894015,-11553689,-21494559,8550130,28346258,1994730 ) + ), + new GroupElementPreComp( + new FieldElement( -19578299,8085545,-14000519,-3948622,2785838,-16231307,-19516951,7174894,22628102,8115180 ), + new FieldElement( -30405132,955511,-11133838,-15078069,-32447087,-13278079,-25651578,3317160,-9943017,930272 ), + new FieldElement( -15303681,-6833769,28856490,1357446,23421993,1057177,24091212,-1388970,-22765376,-10650715 ) + ), + new GroupElementPreComp( + new FieldElement( -22751231,-5303997,-12907607,-12768866,-15811511,-7797053,-14839018,-16554220,-1867018,8398970 ), + new FieldElement( -31969310,2106403,-4736360,1362501,12813763,16200670,22981545,-6291273,18009408,-15772772 ), + new FieldElement( -17220923,-9545221,-27784654,14166835,29815394,7444469,29551787,-3727419,19288549,1325865 ) + ), + new GroupElementPreComp( + new FieldElement( 15100157,-15835752,-23923978,-1005098,-26450192,15509408,12376730,-3479146,33166107,-8042750 ), + new FieldElement( 20909231,13023121,-9209752,16251778,-5778415,-8094914,12412151,10018715,2213263,-13878373 ), + new FieldElement( 32529814,-11074689,30361439,-16689753,-9135940,1513226,22922121,6382134,-5766928,8371348 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 9923462,11271500,12616794,3544722,-29998368,-1721626,12891687,-8193132,-26442943,10486144 ), + new FieldElement( -22597207,-7012665,8587003,-8257861,4084309,-12970062,361726,2610596,-23921530,-11455195 ), + new FieldElement( 5408411,-1136691,-4969122,10561668,24145918,14240566,31319731,-4235541,19985175,-3436086 ) + ), + new GroupElementPreComp( + new FieldElement( -13994457,16616821,14549246,3341099,32155958,13648976,-17577068,8849297,65030,8370684 ), + new FieldElement( -8320926,-12049626,31204563,5839400,-20627288,-1057277,-19442942,6922164,12743482,-9800518 ), + new FieldElement( -2361371,12678785,28815050,4759974,-23893047,4884717,23783145,11038569,18800704,255233 ) + ), + new GroupElementPreComp( + new FieldElement( -5269658,-1773886,13957886,7990715,23132995,728773,13393847,9066957,19258688,-14753793 ), + new FieldElement( -2936654,-10827535,-10432089,14516793,-3640786,4372541,-31934921,2209390,-1524053,2055794 ), + new FieldElement( 580882,16705327,5468415,-2683018,-30926419,-14696000,-7203346,-8994389,-30021019,7394435 ) + ), + new GroupElementPreComp( + new FieldElement( 23838809,1822728,-15738443,15242727,8318092,-3733104,-21672180,-3492205,-4821741,14799921 ), + new FieldElement( 13345610,9759151,3371034,-16137791,16353039,8577942,31129804,13496856,-9056018,7402518 ), + new FieldElement( 2286874,-4435931,-20042458,-2008336,-13696227,5038122,11006906,-15760352,8205061,1607563 ) + ), + new GroupElementPreComp( + new FieldElement( 14414086,-8002132,3331830,-3208217,22249151,-5594188,18364661,-2906958,30019587,-9029278 ), + new FieldElement( -27688051,1585953,-10775053,931069,-29120221,-11002319,-14410829,12029093,9944378,8024 ), + new FieldElement( 4368715,-3709630,29874200,-15022983,-20230386,-11410704,-16114594,-999085,-8142388,5640030 ) + ), + new GroupElementPreComp( + new FieldElement( 10299610,13746483,11661824,16234854,7630238,5998374,9809887,-16694564,15219798,-14327783 ), + new FieldElement( 27425505,-5719081,3055006,10660664,23458024,595578,-15398605,-1173195,-18342183,9742717 ), + new FieldElement( 6744077,2427284,26042789,2720740,-847906,1118974,32324614,7406442,12420155,1994844 ) + ), + new GroupElementPreComp( + new FieldElement( 14012521,-5024720,-18384453,-9578469,-26485342,-3936439,-13033478,-10909803,24319929,-6446333 ), + new FieldElement( 16412690,-4507367,10772641,15929391,-17068788,-4658621,10555945,-10484049,-30102368,-4739048 ), + new FieldElement( 22397382,-7767684,-9293161,-12792868,17166287,-9755136,-27333065,6199366,21880021,-12250760 ) + ), + new GroupElementPreComp( + new FieldElement( -4283307,5368523,-31117018,8163389,-30323063,3209128,16557151,8890729,8840445,4957760 ), + new FieldElement( -15447727,709327,-6919446,-10870178,-29777922,6522332,-21720181,12130072,-14796503,5005757 ), + new FieldElement( -2114751,-14308128,23019042,15765735,-25269683,6002752,10183197,-13239326,-16395286,-2176112 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -19025756,1632005,13466291,-7995100,-23640451,16573537,-32013908,-3057104,22208662,2000468 ), + new FieldElement( 3065073,-1412761,-25598674,-361432,-17683065,-5703415,-8164212,11248527,-3691214,-7414184 ), + new FieldElement( 10379208,-6045554,8877319,1473647,-29291284,-12507580,16690915,2553332,-3132688,16400289 ) + ), + new GroupElementPreComp( + new FieldElement( 15716668,1254266,-18472690,7446274,-8448918,6344164,-22097271,-7285580,26894937,9132066 ), + new FieldElement( 24158887,12938817,11085297,-8177598,-28063478,-4457083,-30576463,64452,-6817084,-2692882 ), + new FieldElement( 13488534,7794716,22236231,5989356,25426474,-12578208,2350710,-3418511,-4688006,2364226 ) + ), + new GroupElementPreComp( + new FieldElement( 16335052,9132434,25640582,6678888,1725628,8517937,-11807024,-11697457,15445875,-7798101 ), + new FieldElement( 29004207,-7867081,28661402,-640412,-12794003,-7943086,31863255,-4135540,-278050,-15759279 ), + new FieldElement( -6122061,-14866665,-28614905,14569919,-10857999,-3591829,10343412,-6976290,-29828287,-10815811 ) + ), + new GroupElementPreComp( + new FieldElement( 27081650,3463984,14099042,-4517604,1616303,-6205604,29542636,15372179,17293797,960709 ), + new FieldElement( 20263915,11434237,-5765435,11236810,13505955,-10857102,-16111345,6493122,-19384511,7639714 ), + new FieldElement( -2830798,-14839232,25403038,-8215196,-8317012,-16173699,18006287,-16043750,29994677,-15808121 ) + ), + new GroupElementPreComp( + new FieldElement( 9769828,5202651,-24157398,-13631392,-28051003,-11561624,-24613141,-13860782,-31184575,709464 ), + new FieldElement( 12286395,13076066,-21775189,-1176622,-25003198,4057652,-32018128,-8890874,16102007,13205847 ), + new FieldElement( 13733362,5599946,10557076,3195751,-5557991,8536970,-25540170,8525972,10151379,10394400 ) + ), + new GroupElementPreComp( + new FieldElement( 4024660,-16137551,22436262,12276534,-9099015,-2686099,19698229,11743039,-33302334,8934414 ), + new FieldElement( -15879800,-4525240,-8580747,-2934061,14634845,-698278,-9449077,3137094,-11536886,11721158 ), + new FieldElement( 17555939,-5013938,8268606,2331751,-22738815,9761013,9319229,8835153,-9205489,-1280045 ) + ), + new GroupElementPreComp( + new FieldElement( -461409,-7830014,20614118,16688288,-7514766,-4807119,22300304,505429,6108462,-6183415 ), + new FieldElement( -5070281,12367917,-30663534,3234473,32617080,-8422642,29880583,-13483331,-26898490,-7867459 ), + new FieldElement( -31975283,5726539,26934134,10237677,-3173717,-605053,24199304,3795095,7592688,-14992079 ) + ), + new GroupElementPreComp( + new FieldElement( 21594432,-14964228,17466408,-4077222,32537084,2739898,6407723,12018833,-28256052,4298412 ), + new FieldElement( -20650503,-11961496,-27236275,570498,3767144,-1717540,13891942,-1569194,13717174,10805743 ), + new FieldElement( -14676630,-15644296,15287174,11927123,24177847,-8175568,-796431,14860609,-26938930,-5863836 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 12962541,5311799,-10060768,11658280,18855286,-7954201,13286263,-12808704,-4381056,9882022 ), + new FieldElement( 18512079,11319350,-20123124,15090309,18818594,5271736,-22727904,3666879,-23967430,-3299429 ), + new FieldElement( -6789020,-3146043,16192429,13241070,15898607,-14206114,-10084880,-6661110,-2403099,5276065 ) + ), + new GroupElementPreComp( + new FieldElement( 30169808,-5317648,26306206,-11750859,27814964,7069267,7152851,3684982,1449224,13082861 ), + new FieldElement( 10342826,3098505,2119311,193222,25702612,12233820,23697382,15056736,-21016438,-8202000 ), + new FieldElement( -33150110,3261608,22745853,7948688,19370557,-15177665,-26171976,6482814,-10300080,-11060101 ) + ), + new GroupElementPreComp( + new FieldElement( 32869458,-5408545,25609743,15678670,-10687769,-15471071,26112421,2521008,-22664288,6904815 ), + new FieldElement( 29506923,4457497,3377935,-9796444,-30510046,12935080,1561737,3841096,-29003639,-6657642 ), + new FieldElement( 10340844,-6630377,-18656632,-2278430,12621151,-13339055,30878497,-11824370,-25584551,5181966 ) + ), + new GroupElementPreComp( + new FieldElement( 25940115,-12658025,17324188,-10307374,-8671468,15029094,24396252,-16450922,-2322852,-12388574 ), + new FieldElement( -21765684,9916823,-1300409,4079498,-1028346,11909559,1782390,12641087,20603771,-6561742 ), + new FieldElement( -18882287,-11673380,24849422,11501709,13161720,-4768874,1925523,11914390,4662781,7820689 ) + ), + new GroupElementPreComp( + new FieldElement( 12241050,-425982,8132691,9393934,32846760,-1599620,29749456,12172924,16136752,15264020 ), + new FieldElement( -10349955,-14680563,-8211979,2330220,-17662549,-14545780,10658213,6671822,19012087,3772772 ), + new FieldElement( 3753511,-3421066,10617074,2028709,14841030,-6721664,28718732,-15762884,20527771,12988982 ) + ), + new GroupElementPreComp( + new FieldElement( -14822485,-5797269,-3707987,12689773,-898983,-10914866,-24183046,-10564943,3299665,-12424953 ), + new FieldElement( -16777703,-15253301,-9642417,4978983,3308785,8755439,6943197,6461331,-25583147,8991218 ), + new FieldElement( -17226263,1816362,-1673288,-6086439,31783888,-8175991,-32948145,7417950,-30242287,1507265 ) + ), + new GroupElementPreComp( + new FieldElement( 29692663,6829891,-10498800,4334896,20945975,-11906496,-28887608,8209391,14606362,-10647073 ), + new FieldElement( -3481570,8707081,32188102,5672294,22096700,1711240,-33020695,9761487,4170404,-2085325 ), + new FieldElement( -11587470,14855945,-4127778,-1531857,-26649089,15084046,22186522,16002000,-14276837,-8400798 ) + ), + new GroupElementPreComp( + new FieldElement( -4811456,13761029,-31703877,-2483919,-3312471,7869047,-7113572,-9620092,13240845,10965870 ), + new FieldElement( -7742563,-8256762,-14768334,-13656260,-23232383,12387166,4498947,14147411,29514390,4302863 ), + new FieldElement( -13413405,-12407859,20757302,-13801832,14785143,8976368,-5061276,-2144373,17846988,-13971927 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -2244452,-754728,-4597030,-1066309,-6247172,1455299,-21647728,-9214789,-5222701,12650267 ), + new FieldElement( -9906797,-16070310,21134160,12198166,-27064575,708126,387813,13770293,-19134326,10958663 ), + new FieldElement( 22470984,12369526,23446014,-5441109,-21520802,-9698723,-11772496,-11574455,-25083830,4271862 ) + ), + new GroupElementPreComp( + new FieldElement( -25169565,-10053642,-19909332,15361595,-5984358,2159192,75375,-4278529,-32526221,8469673 ), + new FieldElement( 15854970,4148314,-8893890,7259002,11666551,13824734,-30531198,2697372,24154791,-9460943 ), + new FieldElement( 15446137,-15806644,29759747,14019369,30811221,-9610191,-31582008,12840104,24913809,9815020 ) + ), + new GroupElementPreComp( + new FieldElement( -4709286,-5614269,-31841498,-12288893,-14443537,10799414,-9103676,13438769,18735128,9466238 ), + new FieldElement( 11933045,9281483,5081055,-5183824,-2628162,-4905629,-7727821,-10896103,-22728655,16199064 ), + new FieldElement( 14576810,379472,-26786533,-8317236,-29426508,-10812974,-102766,1876699,30801119,2164795 ) + ), + new GroupElementPreComp( + new FieldElement( 15995086,3199873,13672555,13712240,-19378835,-4647646,-13081610,-15496269,-13492807,1268052 ), + new FieldElement( -10290614,-3659039,-3286592,10948818,23037027,3794475,-3470338,-12600221,-17055369,3565904 ), + new FieldElement( 29210088,-9419337,-5919792,-4952785,10834811,-13327726,-16512102,-10820713,-27162222,-14030531 ) + ), + new GroupElementPreComp( + new FieldElement( -13161890,15508588,16663704,-8156150,-28349942,9019123,-29183421,-3769423,2244111,-14001979 ), + new FieldElement( -5152875,-3800936,-9306475,-6071583,16243069,14684434,-25673088,-16180800,13491506,4641841 ), + new FieldElement( 10813417,643330,-19188515,-728916,30292062,-16600078,27548447,-7721242,14476989,-12767431 ) + ), + new GroupElementPreComp( + new FieldElement( 10292079,9984945,6481436,8279905,-7251514,7032743,27282937,-1644259,-27912810,12651324 ), + new FieldElement( -31185513,-813383,22271204,11835308,10201545,15351028,17099662,3988035,21721536,-3148940 ), + new FieldElement( 10202177,-6545839,-31373232,-9574638,-32150642,-8119683,-12906320,3852694,13216206,14842320 ) + ), + new GroupElementPreComp( + new FieldElement( -15815640,-10601066,-6538952,-7258995,-6984659,-6581778,-31500847,13765824,-27434397,9900184 ), + new FieldElement( 14465505,-13833331,-32133984,-14738873,-27443187,12990492,33046193,15796406,-7051866,-8040114 ), + new FieldElement( 30924417,-8279620,6359016,-12816335,16508377,9071735,-25488601,15413635,9524356,-7018878 ) + ), + new GroupElementPreComp( + new FieldElement( 12274201,-13175547,32627641,-1785326,6736625,13267305,5237659,-5109483,15663516,4035784 ), + new FieldElement( -2951309,8903985,17349946,601635,-16432815,-4612556,-13732739,-15889334,-22258478,4659091 ), + new FieldElement( -16916263,-4952973,-30393711,-15158821,20774812,15897498,5736189,15026997,-2178256,-13455585 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -8858980,-2219056,28571666,-10155518,-474467,-10105698,-3801496,278095,23440562,-290208 ), + new FieldElement( 10226241,-5928702,15139956,120818,-14867693,5218603,32937275,11551483,-16571960,-7442864 ), + new FieldElement( 17932739,-12437276,-24039557,10749060,11316803,7535897,22503767,5561594,-3646624,3898661 ) + ), + new GroupElementPreComp( + new FieldElement( 7749907,-969567,-16339731,-16464,-25018111,15122143,-1573531,7152530,21831162,1245233 ), + new FieldElement( 26958459,-14658026,4314586,8346991,-5677764,11960072,-32589295,-620035,-30402091,-16716212 ), + new FieldElement( -12165896,9166947,33491384,13673479,29787085,13096535,6280834,14587357,-22338025,13987525 ) + ), + new GroupElementPreComp( + new FieldElement( -24349909,7778775,21116000,15572597,-4833266,-5357778,-4300898,-5124639,-7469781,-2858068 ), + new FieldElement( 9681908,-6737123,-31951644,13591838,-6883821,386950,31622781,6439245,-14581012,4091397 ), + new FieldElement( -8426427,1470727,-28109679,-1596990,3978627,-5123623,-19622683,12092163,29077877,-14741988 ) + ), + new GroupElementPreComp( + new FieldElement( 5269168,-6859726,-13230211,-8020715,25932563,1763552,-5606110,-5505881,-20017847,2357889 ), + new FieldElement( 32264008,-15407652,-5387735,-1160093,-2091322,-3946900,23104804,-12869908,5727338,189038 ), + new FieldElement( 14609123,-8954470,-6000566,-16622781,-14577387,-7743898,-26745169,10942115,-25888931,-14884697 ) + ), + new GroupElementPreComp( + new FieldElement( 20513500,5557931,-15604613,7829531,26413943,-2019404,-21378968,7471781,13913677,-5137875 ), + new FieldElement( -25574376,11967826,29233242,12948236,-6754465,4713227,-8940970,14059180,12878652,8511905 ), + new FieldElement( -25656801,3393631,-2955415,-7075526,-2250709,9366908,-30223418,6812974,5568676,-3127656 ) + ), + new GroupElementPreComp( + new FieldElement( 11630004,12144454,2116339,13606037,27378885,15676917,-17408753,-13504373,-14395196,8070818 ), + new FieldElement( 27117696,-10007378,-31282771,-5570088,1127282,12772488,-29845906,10483306,-11552749,-1028714 ), + new FieldElement( 10637467,-5688064,5674781,1072708,-26343588,-6982302,-1683975,9177853,-27493162,15431203 ) + ), + new GroupElementPreComp( + new FieldElement( 20525145,10892566,-12742472,12779443,-29493034,16150075,-28240519,14943142,-15056790,-7935931 ), + new FieldElement( -30024462,5626926,-551567,-9981087,753598,11981191,25244767,-3239766,-3356550,9594024 ), + new FieldElement( -23752644,2636870,-5163910,-10103818,585134,7877383,11345683,-6492290,13352335,-10977084 ) + ), + new GroupElementPreComp( + new FieldElement( -1931799,-5407458,3304649,-12884869,17015806,-4877091,-29783850,-7752482,-13215537,-319204 ), + new FieldElement( 20239939,6607058,6203985,3483793,-18386976,-779229,-20723742,15077870,-22750759,14523817 ), + new FieldElement( 27406042,-6041657,27423596,-4497394,4996214,10002360,-28842031,-4545494,-30172742,-4805667 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 11374242,12660715,17861383,-12540833,10935568,1099227,-13886076,-9091740,-27727044,11358504 ), + new FieldElement( -12730809,10311867,1510375,10778093,-2119455,-9145702,32676003,11149336,-26123651,4985768 ), + new FieldElement( -19096303,341147,-6197485,-239033,15756973,-8796662,-983043,13794114,-19414307,-15621255 ) + ), + new GroupElementPreComp( + new FieldElement( 6490081,11940286,25495923,-7726360,8668373,-8751316,3367603,6970005,-1691065,-9004790 ), + new FieldElement( 1656497,13457317,15370807,6364910,13605745,8362338,-19174622,-5475723,-16796596,-5031438 ), + new FieldElement( -22273315,-13524424,-64685,-4334223,-18605636,-10921968,-20571065,-7007978,-99853,-10237333 ) + ), + new GroupElementPreComp( + new FieldElement( 17747465,10039260,19368299,-4050591,-20630635,-16041286,31992683,-15857976,-29260363,-5511971 ), + new FieldElement( 31932027,-4986141,-19612382,16366580,22023614,88450,11371999,-3744247,4882242,-10626905 ), + new FieldElement( 29796507,37186,19818052,10115756,-11829032,3352736,18551198,3272828,-5190932,-4162409 ) + ), + new GroupElementPreComp( + new FieldElement( 12501286,4044383,-8612957,-13392385,-32430052,5136599,-19230378,-3529697,330070,-3659409 ), + new FieldElement( 6384877,2899513,17807477,7663917,-2358888,12363165,25366522,-8573892,-271295,12071499 ), + new FieldElement( -8365515,-4042521,25133448,-4517355,-6211027,2265927,-32769618,1936675,-5159697,3829363 ) + ), + new GroupElementPreComp( + new FieldElement( 28425966,-5835433,-577090,-4697198,-14217555,6870930,7921550,-6567787,26333140,14267664 ), + new FieldElement( -11067219,11871231,27385719,-10559544,-4585914,-11189312,10004786,-8709488,-21761224,8930324 ), + new FieldElement( -21197785,-16396035,25654216,-1725397,12282012,11008919,1541940,4757911,-26491501,-16408940 ) + ), + new GroupElementPreComp( + new FieldElement( 13537262,-7759490,-20604840,10961927,-5922820,-13218065,-13156584,6217254,-15943699,13814990 ), + new FieldElement( -17422573,15157790,18705543,29619,24409717,-260476,27361681,9257833,-1956526,-1776914 ), + new FieldElement( -25045300,-10191966,15366585,15166509,-13105086,8423556,-29171540,12361135,-18685978,4578290 ) + ), + new GroupElementPreComp( + new FieldElement( 24579768,3711570,1342322,-11180126,-27005135,14124956,-22544529,14074919,21964432,8235257 ), + new FieldElement( -6528613,-2411497,9442966,-5925588,12025640,-1487420,-2981514,-1669206,13006806,2355433 ), + new FieldElement( -16304899,-13605259,-6632427,-5142349,16974359,-10911083,27202044,1719366,1141648,-12796236 ) + ), + new GroupElementPreComp( + new FieldElement( -12863944,-13219986,-8318266,-11018091,-6810145,-4843894,13475066,-3133972,32674895,13715045 ), + new FieldElement( 11423335,-5468059,32344216,8962751,24989809,9241752,-13265253,16086212,-28740881,-15642093 ), + new FieldElement( -1409668,12530728,-6368726,10847387,19531186,-14132160,-11709148,7791794,-27245943,4383347 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -28970898,5271447,-1266009,-9736989,-12455236,16732599,-4862407,-4906449,27193557,6245191 ), + new FieldElement( -15193956,5362278,-1783893,2695834,4960227,12840725,23061898,3260492,22510453,8577507 ), + new FieldElement( -12632451,11257346,-32692994,13548177,-721004,10879011,31168030,13952092,-29571492,-3635906 ) + ), + new GroupElementPreComp( + new FieldElement( 3877321,-9572739,32416692,5405324,-11004407,-13656635,3759769,11935320,5611860,8164018 ), + new FieldElement( -16275802,14667797,15906460,12155291,-22111149,-9039718,32003002,-8832289,5773085,-8422109 ), + new FieldElement( -23788118,-8254300,1950875,8937633,18686727,16459170,-905725,12376320,31632953,190926 ) + ), + new GroupElementPreComp( + new FieldElement( -24593607,-16138885,-8423991,13378746,14162407,6901328,-8288749,4508564,-25341555,-3627528 ), + new FieldElement( 8884438,-5884009,6023974,10104341,-6881569,-4941533,18722941,-14786005,-1672488,827625 ), + new FieldElement( -32720583,-16289296,-32503547,7101210,13354605,2659080,-1800575,-14108036,-24878478,1541286 ) + ), + new GroupElementPreComp( + new FieldElement( 2901347,-1117687,3880376,-10059388,-17620940,-3612781,-21802117,-3567481,20456845,-1885033 ), + new FieldElement( 27019610,12299467,-13658288,-1603234,-12861660,-4861471,-19540150,-5016058,29439641,15138866 ), + new FieldElement( 21536104,-6626420,-32447818,-10690208,-22408077,5175814,-5420040,-16361163,7779328,109896 ) + ), + new GroupElementPreComp( + new FieldElement( 30279744,14648750,-8044871,6425558,13639621,-743509,28698390,12180118,23177719,-554075 ), + new FieldElement( 26572847,3405927,-31701700,12890905,-19265668,5335866,-6493768,2378492,4439158,-13279347 ), + new FieldElement( -22716706,3489070,-9225266,-332753,18875722,-1140095,14819434,-12731527,-17717757,-5461437 ) + ), + new GroupElementPreComp( + new FieldElement( -5056483,16566551,15953661,3767752,-10436499,15627060,-820954,2177225,8550082,-15114165 ), + new FieldElement( -18473302,16596775,-381660,15663611,22860960,15585581,-27844109,-3582739,-23260460,-8428588 ), + new FieldElement( -32480551,15707275,-8205912,-5652081,29464558,2713815,-22725137,15860482,-21902570,1494193 ) + ), + new GroupElementPreComp( + new FieldElement( -19562091,-14087393,-25583872,-9299552,13127842,759709,21923482,16529112,8742704,12967017 ), + new FieldElement( -28464899,1553205,32536856,-10473729,-24691605,-406174,-8914625,-2933896,-29903758,15553883 ), + new FieldElement( 21877909,3230008,9881174,10539357,-4797115,2841332,11543572,14513274,19375923,-12647961 ) + ), + new GroupElementPreComp( + new FieldElement( 8832269,-14495485,13253511,5137575,5037871,4078777,24880818,-6222716,2862653,9455043 ), + new FieldElement( 29306751,5123106,20245049,-14149889,9592566,8447059,-2077124,-2990080,15511449,4789663 ), + new FieldElement( -20679756,7004547,8824831,-9434977,-4045704,-3750736,-5754762,108893,23513200,16652362 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -33256173,4144782,-4476029,-6579123,10770039,-7155542,-6650416,-12936300,-18319198,10212860 ), + new FieldElement( 2756081,8598110,7383731,-6859892,22312759,-1105012,21179801,2600940,-9988298,-12506466 ), + new FieldElement( -24645692,13317462,-30449259,-15653928,21365574,-10869657,11344424,864440,-2499677,-16710063 ) + ), + new GroupElementPreComp( + new FieldElement( -26432803,6148329,-17184412,-14474154,18782929,-275997,-22561534,211300,2719757,4940997 ), + new FieldElement( -1323882,3911313,-6948744,14759765,-30027150,7851207,21690126,8518463,26699843,5276295 ), + new FieldElement( -13149873,-6429067,9396249,365013,24703301,-10488939,1321586,149635,-15452774,7159369 ) + ), + new GroupElementPreComp( + new FieldElement( 9987780,-3404759,17507962,9505530,9731535,-2165514,22356009,8312176,22477218,-8403385 ), + new FieldElement( 18155857,-16504990,19744716,9006923,15154154,-10538976,24256460,-4864995,-22548173,9334109 ), + new FieldElement( 2986088,-4911893,10776628,-3473844,10620590,-7083203,-21413845,14253545,-22587149,536906 ) + ), + new GroupElementPreComp( + new FieldElement( 4377756,8115836,24567078,15495314,11625074,13064599,7390551,10589625,10838060,-15420424 ), + new FieldElement( -19342404,867880,9277171,-3218459,-14431572,-1986443,19295826,-15796950,6378260,699185 ), + new FieldElement( 7895026,4057113,-7081772,-13077756,-17886831,-323126,-716039,15693155,-5045064,-13373962 ) + ), + new GroupElementPreComp( + new FieldElement( -7737563,-5869402,-14566319,-7406919,11385654,13201616,31730678,-10962840,-3918636,-9669325 ), + new FieldElement( 10188286,-15770834,-7336361,13427543,22223443,14896287,30743455,7116568,-21786507,5427593 ), + new FieldElement( 696102,13206899,27047647,-10632082,15285305,-9853179,10798490,-4578720,19236243,12477404 ) + ), + new GroupElementPreComp( + new FieldElement( -11229439,11243796,-17054270,-8040865,-788228,-8167967,-3897669,11180504,-23169516,7733644 ), + new FieldElement( 17800790,-14036179,-27000429,-11766671,23887827,3149671,23466177,-10538171,10322027,15313801 ), + new FieldElement( 26246234,11968874,32263343,-5468728,6830755,-13323031,-15794704,-101982,-24449242,10890804 ) + ), + new GroupElementPreComp( + new FieldElement( -31365647,10271363,-12660625,-6267268,16690207,-13062544,-14982212,16484931,25180797,-5334884 ), + new FieldElement( -586574,10376444,-32586414,-11286356,19801893,10997610,2276632,9482883,316878,13820577 ), + new FieldElement( -9882808,-4510367,-2115506,16457136,-11100081,11674996,30756178,-7515054,30696930,-3712849 ) + ), + new GroupElementPreComp( + new FieldElement( 32988917,-9603412,12499366,7910787,-10617257,-11931514,-7342816,-9985397,-32349517,7392473 ), + new FieldElement( -8855661,15927861,9866406,-3649411,-2396914,-16655781,-30409476,-9134995,25112947,-2926644 ), + new FieldElement( -2504044,-436966,25621774,-5678772,15085042,-5479877,-24884878,-13526194,5537438,-13914319 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -11225584,2320285,-9584280,10149187,-33444663,5808648,-14876251,-1729667,31234590,6090599 ), + new FieldElement( -9633316,116426,26083934,2897444,-6364437,-2688086,609721,15878753,-6970405,-9034768 ), + new FieldElement( -27757857,247744,-15194774,-9002551,23288161,-10011936,-23869595,6503646,20650474,1804084 ) + ), + new GroupElementPreComp( + new FieldElement( -27589786,15456424,8972517,8469608,15640622,4439847,3121995,-10329713,27842616,-202328 ), + new FieldElement( -15306973,2839644,22530074,10026331,4602058,5048462,28248656,5031932,-11375082,12714369 ), + new FieldElement( 20807691,-7270825,29286141,11421711,-27876523,-13868230,-21227475,1035546,-19733229,12796920 ) + ), + new GroupElementPreComp( + new FieldElement( 12076899,-14301286,-8785001,-11848922,-25012791,16400684,-17591495,-12899438,3480665,-15182815 ), + new FieldElement( -32361549,5457597,28548107,7833186,7303070,-11953545,-24363064,-15921875,-33374054,2771025 ), + new FieldElement( -21389266,421932,26597266,6860826,22486084,-6737172,-17137485,-4210226,-24552282,15673397 ) + ), + new GroupElementPreComp( + new FieldElement( -20184622,2338216,19788685,-9620956,-4001265,-8740893,-20271184,4733254,3727144,-12934448 ), + new FieldElement( 6120119,814863,-11794402,-622716,6812205,-15747771,2019594,7975683,31123697,-10958981 ), + new FieldElement( 30069250,-11435332,30434654,2958439,18399564,-976289,12296869,9204260,-16432438,9648165 ) + ), + new GroupElementPreComp( + new FieldElement( 32705432,-1550977,30705658,7451065,-11805606,9631813,3305266,5248604,-26008332,-11377501 ), + new FieldElement( 17219865,2375039,-31570947,-5575615,-19459679,9219903,294711,15298639,2662509,-16297073 ), + new FieldElement( -1172927,-7558695,-4366770,-4287744,-21346413,-8434326,32087529,-1222777,32247248,-14389861 ) + ), + new GroupElementPreComp( + new FieldElement( 14312628,1221556,17395390,-8700143,-4945741,-8684635,-28197744,-9637817,-16027623,-13378845 ), + new FieldElement( -1428825,-9678990,-9235681,6549687,-7383069,-468664,23046502,9803137,17597934,2346211 ), + new FieldElement( 18510800,15337574,26171504,981392,-22241552,7827556,-23491134,-11323352,3059833,-11782870 ) + ), + new GroupElementPreComp( + new FieldElement( 10141598,6082907,17829293,-1947643,9830092,13613136,-25556636,-5544586,-33502212,3592096 ), + new FieldElement( 33114168,-15889352,-26525686,-13343397,33076705,8716171,1151462,1521897,-982665,-6837803 ), + new FieldElement( -32939165,-4255815,23947181,-324178,-33072974,-12305637,-16637686,3891704,26353178,693168 ) + ), + new GroupElementPreComp( + new FieldElement( 30374239,1595580,-16884039,13186931,4600344,406904,9585294,-400668,31375464,14369965 ), + new FieldElement( -14370654,-7772529,1510301,6434173,-18784789,-6262728,32732230,-13108839,17901441,16011505 ), + new FieldElement( 18171223,-11934626,-12500402,15197122,-11038147,-15230035,-19172240,-16046376,8764035,12309598 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 5975908,-5243188,-19459362,-9681747,-11541277,14015782,-23665757,1228319,17544096,-10593782 ), + new FieldElement( 5811932,-1715293,3442887,-2269310,-18367348,-8359541,-18044043,-15410127,-5565381,12348900 ), + new FieldElement( -31399660,11407555,25755363,6891399,-3256938,14872274,-24849353,8141295,-10632534,-585479 ) + ), + new GroupElementPreComp( + new FieldElement( -12675304,694026,-5076145,13300344,14015258,-14451394,-9698672,-11329050,30944593,1130208 ), + new FieldElement( 8247766,-6710942,-26562381,-7709309,-14401939,-14648910,4652152,2488540,23550156,-271232 ), + new FieldElement( 17294316,-3788438,7026748,15626851,22990044,113481,2267737,-5908146,-408818,-137719 ) + ), + new GroupElementPreComp( + new FieldElement( 16091085,-16253926,18599252,7340678,2137637,-1221657,-3364161,14550936,3260525,-7166271 ), + new FieldElement( -4910104,-13332887,18550887,10864893,-16459325,-7291596,-23028869,-13204905,-12748722,2701326 ), + new FieldElement( -8574695,16099415,4629974,-16340524,-20786213,-6005432,-10018363,9276971,11329923,1862132 ) + ), + new GroupElementPreComp( + new FieldElement( 14763076,-15903608,-30918270,3689867,3511892,10313526,-21951088,12219231,-9037963,-940300 ), + new FieldElement( 8894987,-3446094,6150753,3013931,301220,15693451,-31981216,-2909717,-15438168,11595570 ), + new FieldElement( 15214962,3537601,-26238722,-14058872,4418657,-15230761,13947276,10730794,-13489462,-4363670 ) + ), + new GroupElementPreComp( + new FieldElement( -2538306,7682793,32759013,263109,-29984731,-7955452,-22332124,-10188635,977108,699994 ), + new FieldElement( -12466472,4195084,-9211532,550904,-15565337,12917920,19118110,-439841,-30534533,-14337913 ), + new FieldElement( 31788461,-14507657,4799989,7372237,8808585,-14747943,9408237,-10051775,12493932,-5409317 ) + ), + new GroupElementPreComp( + new FieldElement( -25680606,5260744,-19235809,-6284470,-3695942,16566087,27218280,2607121,29375955,6024730 ), + new FieldElement( 842132,-2794693,-4763381,-8722815,26332018,-12405641,11831880,6985184,-9940361,2854096 ), + new FieldElement( -4847262,-7969331,2516242,-5847713,9695691,-7221186,16512645,960770,12121869,16648078 ) + ), + new GroupElementPreComp( + new FieldElement( -15218652,14667096,-13336229,2013717,30598287,-464137,-31504922,-7882064,20237806,2838411 ), + new FieldElement( -19288047,4453152,15298546,-16178388,22115043,-15972604,12544294,-13470457,1068881,-12499905 ), + new FieldElement( -9558883,-16518835,33238498,13506958,30505848,-1114596,-8486907,-2630053,12521378,4845654 ) + ), + new GroupElementPreComp( + new FieldElement( -28198521,10744108,-2958380,10199664,7759311,-13088600,3409348,-873400,-6482306,-12885870 ), + new FieldElement( -23561822,6230156,-20382013,10655314,-24040585,-11621172,10477734,-1240216,-3113227,13974498 ), + new FieldElement( 12966261,15550616,-32038948,-1615346,21025980,-629444,5642325,7188737,18895762,12629579 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 14741879,-14946887,22177208,-11721237,1279741,8058600,11758140,789443,32195181,3895677 ), + new FieldElement( 10758205,15755439,-4509950,9243698,-4879422,6879879,-2204575,-3566119,-8982069,4429647 ), + new FieldElement( -2453894,15725973,-20436342,-10410672,-5803908,-11040220,-7135870,-11642895,18047436,-15281743 ) + ), + new GroupElementPreComp( + new FieldElement( -25173001,-11307165,29759956,11776784,-22262383,-15820455,10993114,-12850837,-17620701,-9408468 ), + new FieldElement( 21987233,700364,-24505048,14972008,-7774265,-5718395,32155026,2581431,-29958985,8773375 ), + new FieldElement( -25568350,454463,-13211935,16126715,25240068,8594567,20656846,12017935,-7874389,-13920155 ) + ), + new GroupElementPreComp( + new FieldElement( 6028182,6263078,-31011806,-11301710,-818919,2461772,-31841174,-5468042,-1721788,-2776725 ), + new FieldElement( -12278994,16624277,987579,-5922598,32908203,1248608,7719845,-4166698,28408820,6816612 ), + new FieldElement( -10358094,-8237829,19549651,-12169222,22082623,16147817,20613181,13982702,-10339570,5067943 ) + ), + new GroupElementPreComp( + new FieldElement( -30505967,-3821767,12074681,13582412,-19877972,2443951,-19719286,12746132,5331210,-10105944 ), + new FieldElement( 30528811,3601899,-1957090,4619785,-27361822,-15436388,24180793,-12570394,27679908,-1648928 ), + new FieldElement( 9402404,-13957065,32834043,10838634,-26580150,-13237195,26653274,-8685565,22611444,-12715406 ) + ), + new GroupElementPreComp( + new FieldElement( 22190590,1118029,22736441,15130463,-30460692,-5991321,19189625,-4648942,4854859,6622139 ), + new FieldElement( -8310738,-2953450,-8262579,-3388049,-10401731,-271929,13424426,-3567227,26404409,13001963 ), + new FieldElement( -31241838,-15415700,-2994250,8939346,11562230,-12840670,-26064365,-11621720,-15405155,11020693 ) + ), + new GroupElementPreComp( + new FieldElement( 1866042,-7949489,-7898649,-10301010,12483315,13477547,3175636,-12424163,28761762,1406734 ), + new FieldElement( -448555,-1777666,13018551,3194501,-9580420,-11161737,24760585,-4347088,25577411,-13378680 ), + new FieldElement( -24290378,4759345,-690653,-1852816,2066747,10693769,-29595790,9884936,-9368926,4745410 ) + ), + new GroupElementPreComp( + new FieldElement( -9141284,6049714,-19531061,-4341411,-31260798,9944276,-15462008,-11311852,10931924,-11931931 ), + new FieldElement( -16561513,14112680,-8012645,4817318,-8040464,-11414606,-22853429,10856641,-20470770,13434654 ), + new FieldElement( 22759489,-10073434,-16766264,-1871422,13637442,-10168091,1765144,-12654326,28445307,-5364710 ) + ), + new GroupElementPreComp( + new FieldElement( 29875063,12493613,2795536,-3786330,1710620,15181182,-10195717,-8788675,9074234,1167180 ), + new FieldElement( -26205683,11014233,-9842651,-2635485,-26908120,7532294,-18716888,-9535498,3843903,9367684 ), + new FieldElement( -10969595,-6403711,9591134,9582310,11349256,108879,16235123,8601684,-139197,4242895 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 22092954,-13191123,-2042793,-11968512,32186753,-11517388,-6574341,2470660,-27417366,16625501 ), + new FieldElement( -11057722,3042016,13770083,-9257922,584236,-544855,-7770857,2602725,-27351616,14247413 ), + new FieldElement( 6314175,-10264892,-32772502,15957557,-10157730,168750,-8618807,14290061,27108877,-1180880 ) + ), + new GroupElementPreComp( + new FieldElement( -8586597,-7170966,13241782,10960156,-32991015,-13794596,33547976,-11058889,-27148451,981874 ), + new FieldElement( 22833440,9293594,-32649448,-13618667,-9136966,14756819,-22928859,-13970780,-10479804,-16197962 ), + new FieldElement( -7768587,3326786,-28111797,10783824,19178761,14905060,22680049,13906969,-15933690,3797899 ) + ), + new GroupElementPreComp( + new FieldElement( 21721356,-4212746,-12206123,9310182,-3882239,-13653110,23740224,-2709232,20491983,-8042152 ), + new FieldElement( 9209270,-15135055,-13256557,-6167798,-731016,15289673,25947805,15286587,30997318,-6703063 ), + new FieldElement( 7392032,16618386,23946583,-8039892,-13265164,-1533858,-14197445,-2321576,17649998,-250080 ) + ), + new GroupElementPreComp( + new FieldElement( -9301088,-14193827,30609526,-3049543,-25175069,-1283752,-15241566,-9525724,-2233253,7662146 ), + new FieldElement( -17558673,1763594,-33114336,15908610,-30040870,-12174295,7335080,-8472199,-3174674,3440183 ), + new FieldElement( -19889700,-5977008,-24111293,-9688870,10799743,-16571957,40450,-4431835,4862400,1133 ) + ), + new GroupElementPreComp( + new FieldElement( -32856209,-7873957,-5422389,14860950,-16319031,7956142,7258061,311861,-30594991,-7379421 ), + new FieldElement( -3773428,-1565936,28985340,7499440,24445838,9325937,29727763,16527196,18278453,15405622 ), + new FieldElement( -4381906,8508652,-19898366,-3674424,-5984453,15149970,-13313598,843523,-21875062,13626197 ) + ), + new GroupElementPreComp( + new FieldElement( 2281448,-13487055,-10915418,-2609910,1879358,16164207,-10783882,3953792,13340839,15928663 ), + new FieldElement( 31727126,-7179855,-18437503,-8283652,2875793,-16390330,-25269894,-7014826,-23452306,5964753 ), + new FieldElement( 4100420,-5959452,-17179337,6017714,-18705837,12227141,-26684835,11344144,2538215,-7570755 ) + ), + new GroupElementPreComp( + new FieldElement( -9433605,6123113,11159803,-2156608,30016280,14966241,-20474983,1485421,-629256,-15958862 ), + new FieldElement( -26804558,4260919,11851389,9658551,-32017107,16367492,-20205425,-13191288,11659922,-11115118 ), + new FieldElement( 26180396,10015009,-30844224,-8581293,5418197,9480663,2231568,-10170080,33100372,-1306171 ) + ), + new GroupElementPreComp( + new FieldElement( 15121113,-5201871,-10389905,15427821,-27509937,-15992507,21670947,4486675,-5931810,-14466380 ), + new FieldElement( 16166486,-9483733,-11104130,6023908,-31926798,-1364923,2340060,-16254968,-10735770,-10039824 ), + new FieldElement( 28042865,-3557089,-12126526,12259706,-3717498,-6945899,6766453,-8689599,18036436,5803270 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -817581,6763912,11803561,1585585,10958447,-2671165,23855391,4598332,-6159431,-14117438 ), + new FieldElement( -31031306,-14256194,17332029,-2383520,31312682,-5967183,696309,50292,-20095739,11763584 ), + new FieldElement( -594563,-2514283,-32234153,12643980,12650761,14811489,665117,-12613632,-19773211,-10713562 ) + ), + new GroupElementPreComp( + new FieldElement( 30464590,-11262872,-4127476,-12734478,19835327,-7105613,-24396175,2075773,-17020157,992471 ), + new FieldElement( 18357185,-6994433,7766382,16342475,-29324918,411174,14578841,8080033,-11574335,-10601610 ), + new FieldElement( 19598397,10334610,12555054,2555664,18821899,-10339780,21873263,16014234,26224780,16452269 ) + ), + new GroupElementPreComp( + new FieldElement( -30223925,5145196,5944548,16385966,3976735,2009897,-11377804,-7618186,-20533829,3698650 ), + new FieldElement( 14187449,3448569,-10636236,-10810935,-22663880,-3433596,7268410,-10890444,27394301,12015369 ), + new FieldElement( 19695761,16087646,28032085,12999827,6817792,11427614,20244189,-1312777,-13259127,-3402461 ) + ), + new GroupElementPreComp( + new FieldElement( 30860103,12735208,-1888245,-4699734,-16974906,2256940,-8166013,12298312,-8550524,-10393462 ), + new FieldElement( -5719826,-11245325,-1910649,15569035,26642876,-7587760,-5789354,-15118654,-4976164,12651793 ), + new FieldElement( -2848395,9953421,11531313,-5282879,26895123,-12697089,-13118820,-16517902,9768698,-2533218 ) + ), + new GroupElementPreComp( + new FieldElement( -24719459,1894651,-287698,-4704085,15348719,-8156530,32767513,12765450,4940095,10678226 ), + new FieldElement( 18860224,15980149,-18987240,-1562570,-26233012,-11071856,-7843882,13944024,-24372348,16582019 ), + new FieldElement( -15504260,4970268,-29893044,4175593,-20993212,-2199756,-11704054,15444560,-11003761,7989037 ) + ), + new GroupElementPreComp( + new FieldElement( 31490452,5568061,-2412803,2182383,-32336847,4531686,-32078269,6200206,-19686113,-14800171 ), + new FieldElement( -17308668,-15879940,-31522777,-2831,-32887382,16375549,8680158,-16371713,28550068,-6857132 ), + new FieldElement( -28126887,-5688091,16837845,-1820458,-6850681,12700016,-30039981,4364038,1155602,5988841 ) + ), + new GroupElementPreComp( + new FieldElement( 21890435,-13272907,-12624011,12154349,-7831873,15300496,23148983,-4470481,24618407,8283181 ), + new FieldElement( -33136107,-10512751,9975416,6841041,-31559793,16356536,3070187,-7025928,1466169,10740210 ), + new FieldElement( -1509399,-15488185,-13503385,-10655916,32799044,909394,-13938903,-5779719,-32164649,-15327040 ) + ), + new GroupElementPreComp( + new FieldElement( 3960823,-14267803,-28026090,-15918051,-19404858,13146868,15567327,951507,-3260321,-573935 ), + new FieldElement( 24740841,5052253,-30094131,8961361,25877428,6165135,-24368180,14397372,-7380369,-6144105 ), + new FieldElement( -28888365,3510803,-28103278,-1158478,-11238128,-10631454,-15441463,-14453128,-1625486,-6494814 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 793299,-9230478,8836302,-6235707,-27360908,-2369593,33152843,-4885251,-9906200,-621852 ), + new FieldElement( 5666233,525582,20782575,-8038419,-24538499,14657740,16099374,1468826,-6171428,-15186581 ), + new FieldElement( -4859255,-3779343,-2917758,-6748019,7778750,11688288,-30404353,-9871238,-1558923,-9863646 ) + ), + new GroupElementPreComp( + new FieldElement( 10896332,-7719704,824275,472601,-19460308,3009587,25248958,14783338,-30581476,-15757844 ), + new FieldElement( 10566929,12612572,-31944212,11118703,-12633376,12362879,21752402,8822496,24003793,14264025 ), + new FieldElement( 27713862,-7355973,-11008240,9227530,27050101,2504721,23886875,-13117525,13958495,-5732453 ) + ), + new GroupElementPreComp( + new FieldElement( -23481610,4867226,-27247128,3900521,29838369,-8212291,-31889399,-10041781,7340521,-15410068 ), + new FieldElement( 4646514,-8011124,-22766023,-11532654,23184553,8566613,31366726,-1381061,-15066784,-10375192 ), + new FieldElement( -17270517,12723032,-16993061,14878794,21619651,-6197576,27584817,3093888,-8843694,3849921 ) + ), + new GroupElementPreComp( + new FieldElement( -9064912,2103172,25561640,-15125738,-5239824,9582958,32477045,-9017955,5002294,-15550259 ), + new FieldElement( -12057553,-11177906,21115585,-13365155,8808712,-12030708,16489530,13378448,-25845716,12741426 ), + new FieldElement( -5946367,10645103,-30911586,15390284,-3286982,-7118677,24306472,15852464,28834118,-7646072 ) + ), + new GroupElementPreComp( + new FieldElement( -17335748,-9107057,-24531279,9434953,-8472084,-583362,-13090771,455841,20461858,5491305 ), + new FieldElement( 13669248,-16095482,-12481974,-10203039,-14569770,-11893198,-24995986,11293807,-28588204,-9421832 ), + new FieldElement( 28497928,6272777,-33022994,14470570,8906179,-1225630,18504674,-14165166,29867745,-8795943 ) + ), + new GroupElementPreComp( + new FieldElement( -16207023,13517196,-27799630,-13697798,24009064,-6373891,-6367600,-13175392,22853429,-4012011 ), + new FieldElement( 24191378,16712145,-13931797,15217831,14542237,1646131,18603514,-11037887,12876623,-2112447 ), + new FieldElement( 17902668,4518229,-411702,-2829247,26878217,5258055,-12860753,608397,16031844,3723494 ) + ), + new GroupElementPreComp( + new FieldElement( -28632773,12763728,-20446446,7577504,33001348,-13017745,17558842,-7872890,23896954,-4314245 ), + new FieldElement( -20005381,-12011952,31520464,605201,2543521,5991821,-2945064,7229064,-9919646,-8826859 ), + new FieldElement( 28816045,298879,-28165016,-15920938,19000928,-1665890,-12680833,-2949325,-18051778,-2082915 ) + ), + new GroupElementPreComp( + new FieldElement( 16000882,-344896,3493092,-11447198,-29504595,-13159789,12577740,16041268,-19715240,7847707 ), + new FieldElement( 10151868,10572098,27312476,7922682,14825339,4723128,-32855931,-6519018,-10020567,3852848 ), + new FieldElement( -11430470,15697596,-21121557,-4420647,5386314,15063598,16514493,-15932110,29330899,-15076224 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -25499735,-4378794,-15222908,-6901211,16615731,2051784,3303702,15490,-27548796,12314391 ), + new FieldElement( 15683520,-6003043,18109120,-9980648,15337968,-5997823,-16717435,15921866,16103996,-3731215 ), + new FieldElement( -23169824,-10781249,13588192,-1628807,-3798557,-1074929,-19273607,5402699,-29815713,-9841101 ) + ), + new GroupElementPreComp( + new FieldElement( 23190676,2384583,-32714340,3462154,-29903655,-1529132,-11266856,8911517,-25205859,2739713 ), + new FieldElement( 21374101,-3554250,-33524649,9874411,15377179,11831242,-33529904,6134907,4931255,11987849 ), + new FieldElement( -7732,-2978858,-16223486,7277597,105524,-322051,-31480539,13861388,-30076310,10117930 ) + ), + new GroupElementPreComp( + new FieldElement( -29501170,-10744872,-26163768,13051539,-25625564,5089643,-6325503,6704079,12890019,15728940 ), + new FieldElement( -21972360,-11771379,-951059,-4418840,14704840,2695116,903376,-10428139,12885167,8311031 ), + new FieldElement( -17516482,5352194,10384213,-13811658,7506451,13453191,26423267,4384730,1888765,-5435404 ) + ), + new GroupElementPreComp( + new FieldElement( -25817338,-3107312,-13494599,-3182506,30896459,-13921729,-32251644,-12707869,-19464434,-3340243 ), + new FieldElement( -23607977,-2665774,-526091,4651136,5765089,4618330,6092245,14845197,17151279,-9854116 ), + new FieldElement( -24830458,-12733720,-15165978,10367250,-29530908,-265356,22825805,-7087279,-16866484,16176525 ) + ), + new GroupElementPreComp( + new FieldElement( -23583256,6564961,20063689,3798228,-4740178,7359225,2006182,-10363426,-28746253,-10197509 ), + new FieldElement( -10626600,-4486402,-13320562,-5125317,3432136,-6393229,23632037,-1940610,32808310,1099883 ), + new FieldElement( 15030977,5768825,-27451236,-2887299,-6427378,-15361371,-15277896,-6809350,2051441,-15225865 ) + ), + new GroupElementPreComp( + new FieldElement( -3362323,-7239372,7517890,9824992,23555850,295369,5148398,-14154188,-22686354,16633660 ), + new FieldElement( 4577086,-16752288,13249841,-15304328,19958763,-14537274,18559670,-10759549,8402478,-9864273 ), + new FieldElement( -28406330,-1051581,-26790155,-907698,-17212414,-11030789,9453451,-14980072,17983010,9967138 ) + ), + new GroupElementPreComp( + new FieldElement( -25762494,6524722,26585488,9969270,24709298,1220360,-1677990,7806337,17507396,3651560 ), + new FieldElement( -10420457,-4118111,14584639,15971087,-15768321,8861010,26556809,-5574557,-18553322,-11357135 ), + new FieldElement( 2839101,14284142,4029895,3472686,14402957,12689363,-26642121,8459447,-5605463,-7621941 ) + ), + new GroupElementPreComp( + new FieldElement( -4839289,-3535444,9744961,2871048,25113978,3187018,-25110813,-849066,17258084,-7977739 ), + new FieldElement( 18164541,-10595176,-17154882,-1542417,19237078,-9745295,23357533,-15217008,26908270,12150756 ), + new FieldElement( -30264870,-7647865,5112249,-7036672,-1499807,-6974257,43168,-5537701,-32302074,16215819 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -6898905,9824394,-12304779,-4401089,-31397141,-6276835,32574489,12532905,-7503072,-8675347 ), + new FieldElement( -27343522,-16515468,-27151524,-10722951,946346,16291093,254968,7168080,21676107,-1943028 ), + new FieldElement( 21260961,-8424752,-16831886,-11920822,-23677961,3968121,-3651949,-6215466,-3556191,-7913075 ) + ), + new GroupElementPreComp( + new FieldElement( 16544754,13250366,-16804428,15546242,-4583003,12757258,-2462308,-8680336,-18907032,-9662799 ), + new FieldElement( -2415239,-15577728,18312303,4964443,-15272530,-12653564,26820651,16690659,25459437,-4564609 ), + new FieldElement( -25144690,11425020,28423002,-11020557,-6144921,-15826224,9142795,-2391602,-6432418,-1644817 ) + ), + new GroupElementPreComp( + new FieldElement( -23104652,6253476,16964147,-3768872,-25113972,-12296437,-27457225,-16344658,6335692,7249989 ), + new FieldElement( -30333227,13979675,7503222,-12368314,-11956721,-4621693,-30272269,2682242,25993170,-12478523 ), + new FieldElement( 4364628,5930691,32304656,-10044554,-8054781,15091131,22857016,-10598955,31820368,15075278 ) + ), + new GroupElementPreComp( + new FieldElement( 31879134,-8918693,17258761,90626,-8041836,-4917709,24162788,-9650886,-17970238,12833045 ), + new FieldElement( 19073683,14851414,-24403169,-11860168,7625278,11091125,-19619190,2074449,-9413939,14905377 ), + new FieldElement( 24483667,-11935567,-2518866,-11547418,-1553130,15355506,-25282080,9253129,27628530,-7555480 ) + ), + new GroupElementPreComp( + new FieldElement( 17597607,8340603,19355617,552187,26198470,-3176583,4593324,-9157582,-14110875,15297016 ), + new FieldElement( 510886,14337390,-31785257,16638632,6328095,2713355,-20217417,-11864220,8683221,2921426 ), + new FieldElement( 18606791,11874196,27155355,-5281482,-24031742,6265446,-25178240,-1278924,4674690,13890525 ) + ), + new GroupElementPreComp( + new FieldElement( 13609624,13069022,-27372361,-13055908,24360586,9592974,14977157,9835105,4389687,288396 ), + new FieldElement( 9922506,-519394,13613107,5883594,-18758345,-434263,-12304062,8317628,23388070,16052080 ), + new FieldElement( 12720016,11937594,-31970060,-5028689,26900120,8561328,-20155687,-11632979,-14754271,-10812892 ) + ), + new GroupElementPreComp( + new FieldElement( 15961858,14150409,26716931,-665832,-22794328,13603569,11829573,7467844,-28822128,929275 ), + new FieldElement( 11038231,-11582396,-27310482,-7316562,-10498527,-16307831,-23479533,-9371869,-21393143,2465074 ), + new FieldElement( 20017163,-4323226,27915242,1529148,12396362,15675764,13817261,-9658066,2463391,-4622140 ) + ), + new GroupElementPreComp( + new FieldElement( -16358878,-12663911,-12065183,4996454,-1256422,1073572,9583558,12851107,4003896,12673717 ), + new FieldElement( -1731589,-15155870,-3262930,16143082,19294135,13385325,14741514,-9103726,7903886,2348101 ), + new FieldElement( 24536016,-16515207,12715592,-3862155,1511293,10047386,-3842346,-7129159,-28377538,10048127 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -12622226,-6204820,30718825,2591312,-10617028,12192840,18873298,-7297090,-32297756,15221632 ), + new FieldElement( -26478122,-11103864,11546244,-1852483,9180880,7656409,-21343950,2095755,29769758,6593415 ), + new FieldElement( -31994208,-2907461,4176912,3264766,12538965,-868111,26312345,-6118678,30958054,8292160 ) + ), + new GroupElementPreComp( + new FieldElement( 31429822,-13959116,29173532,15632448,12174511,-2760094,32808831,3977186,26143136,-3148876 ), + new FieldElement( 22648901,1402143,-22799984,13746059,7936347,365344,-8668633,-1674433,-3758243,-2304625 ), + new FieldElement( -15491917,8012313,-2514730,-12702462,-23965846,-10254029,-1612713,-1535569,-16664475,8194478 ) + ), + new GroupElementPreComp( + new FieldElement( 27338066,-7507420,-7414224,10140405,-19026427,-6589889,27277191,8855376,28572286,3005164 ), + new FieldElement( 26287124,4821776,25476601,-4145903,-3764513,-15788984,-18008582,1182479,-26094821,-13079595 ), + new FieldElement( -7171154,3178080,23970071,6201893,-17195577,-4489192,-21876275,-13982627,32208683,-1198248 ) + ), + new GroupElementPreComp( + new FieldElement( -16657702,2817643,-10286362,14811298,6024667,13349505,-27315504,-10497842,-27672585,-11539858 ), + new FieldElement( 15941029,-9405932,-21367050,8062055,31876073,-238629,-15278393,-1444429,15397331,-4130193 ), + new FieldElement( 8934485,-13485467,-23286397,-13423241,-32446090,14047986,31170398,-1441021,-27505566,15087184 ) + ), + new GroupElementPreComp( + new FieldElement( -18357243,-2156491,24524913,-16677868,15520427,-6360776,-15502406,11461896,16788528,-5868942 ), + new FieldElement( -1947386,16013773,21750665,3714552,-17401782,-16055433,-3770287,-10323320,31322514,-11615635 ), + new FieldElement( 21426655,-5650218,-13648287,-5347537,-28812189,-4920970,-18275391,-14621414,13040862,-12112948 ) + ), + new GroupElementPreComp( + new FieldElement( 11293895,12478086,-27136401,15083750,-29307421,14748872,14555558,-13417103,1613711,4896935 ), + new FieldElement( -25894883,15323294,-8489791,-8057900,25967126,-13425460,2825960,-4897045,-23971776,-11267415 ), + new FieldElement( -15924766,-5229880,-17443532,6410664,3622847,10243618,20615400,12405433,-23753030,-8436416 ) + ), + new GroupElementPreComp( + new FieldElement( -7091295,12556208,-20191352,9025187,-17072479,4333801,4378436,2432030,23097949,-566018 ), + new FieldElement( 4565804,-16025654,20084412,-7842817,1724999,189254,24767264,10103221,-18512313,2424778 ), + new FieldElement( 366633,-11976806,8173090,-6890119,30788634,5745705,-7168678,1344109,-3642553,12412659 ) + ), + new GroupElementPreComp( + new FieldElement( -24001791,7690286,14929416,-168257,-32210835,-13412986,24162697,-15326504,-3141501,11179385 ), + new FieldElement( 18289522,-14724954,8056945,16430056,-21729724,7842514,-6001441,-1486897,-18684645,-11443503 ), + new FieldElement( 476239,6601091,-6152790,-9723375,17503545,-4863900,27672959,13403813,11052904,5219329 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 20678546,-8375738,-32671898,8849123,-5009758,14574752,31186971,-3973730,9014762,-8579056 ), + new FieldElement( -13644050,-10350239,-15962508,5075808,-1514661,-11534600,-33102500,9160280,8473550,-3256838 ), + new FieldElement( 24900749,14435722,17209120,-15292541,-22592275,9878983,-7689309,-16335821,-24568481,11788948 ) + ), + new GroupElementPreComp( + new FieldElement( -3118155,-11395194,-13802089,14797441,9652448,-6845904,-20037437,10410733,-24568470,-1458691 ), + new FieldElement( -15659161,16736706,-22467150,10215878,-9097177,7563911,11871841,-12505194,-18513325,8464118 ), + new FieldElement( -23400612,8348507,-14585951,-861714,-3950205,-6373419,14325289,8628612,33313881,-8370517 ) + ), + new GroupElementPreComp( + new FieldElement( -20186973,-4967935,22367356,5271547,-1097117,-4788838,-24805667,-10236854,-8940735,-5818269 ), + new FieldElement( -6948785,-1795212,-32625683,-16021179,32635414,-7374245,15989197,-12838188,28358192,-4253904 ), + new FieldElement( -23561781,-2799059,-32351682,-1661963,-9147719,10429267,-16637684,4072016,-5351664,5596589 ) + ), + new GroupElementPreComp( + new FieldElement( -28236598,-3390048,12312896,6213178,3117142,16078565,29266239,2557221,1768301,15373193 ), + new FieldElement( -7243358,-3246960,-4593467,-7553353,-127927,-912245,-1090902,-4504991,-24660491,3442910 ), + new FieldElement( -30210571,5124043,14181784,8197961,18964734,-11939093,22597931,7176455,-18585478,13365930 ) + ), + new GroupElementPreComp( + new FieldElement( -7877390,-1499958,8324673,4690079,6261860,890446,24538107,-8570186,-9689599,-3031667 ), + new FieldElement( 25008904,-10771599,-4305031,-9638010,16265036,15721635,683793,-11823784,15723479,-15163481 ), + new FieldElement( -9660625,12374379,-27006999,-7026148,-7724114,-12314514,11879682,5400171,519526,-1235876 ) + ), + new GroupElementPreComp( + new FieldElement( 22258397,-16332233,-7869817,14613016,-22520255,-2950923,-20353881,7315967,16648397,7605640 ), + new FieldElement( -8081308,-8464597,-8223311,9719710,19259459,-15348212,23994942,-5281555,-9468848,4763278 ), + new FieldElement( -21699244,9220969,-15730624,1084137,-25476107,-2852390,31088447,-7764523,-11356529,728112 ) + ), + new GroupElementPreComp( + new FieldElement( 26047220,-11751471,-6900323,-16521798,24092068,9158119,-4273545,-12555558,-29365436,-5498272 ), + new FieldElement( 17510331,-322857,5854289,8403524,17133918,-3112612,-28111007,12327945,10750447,10014012 ), + new FieldElement( -10312768,3936952,9156313,-8897683,16498692,-994647,-27481051,-666732,3424691,7540221 ) + ), + new GroupElementPreComp( + new FieldElement( 30322361,-6964110,11361005,-4143317,7433304,4989748,-7071422,-16317219,-9244265,15258046 ), + new FieldElement( 13054562,-2779497,19155474,469045,-12482797,4566042,5631406,2711395,1062915,-5136345 ), + new FieldElement( -19240248,-11254599,-29509029,-7499965,-5835763,13005411,-6066489,12194497,32960380,1459310 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 19852034,7027924,23669353,10020366,8586503,-6657907,394197,-6101885,18638003,-11174937 ), + new FieldElement( 31395534,15098109,26581030,8030562,-16527914,-5007134,9012486,-7584354,-6643087,-5442636 ), + new FieldElement( -9192165,-2347377,-1997099,4529534,25766844,607986,-13222,9677543,-32294889,-6456008 ) + ), + new GroupElementPreComp( + new FieldElement( -2444496,-149937,29348902,8186665,1873760,12489863,-30934579,-7839692,-7852844,-8138429 ), + new FieldElement( -15236356,-15433509,7766470,746860,26346930,-10221762,-27333451,10754588,-9431476,5203576 ), + new FieldElement( 31834314,14135496,-770007,5159118,20917671,-16768096,-7467973,-7337524,31809243,7347066 ) + ), + new GroupElementPreComp( + new FieldElement( -9606723,-11874240,20414459,13033986,13716524,-11691881,19797970,-12211255,15192876,-2087490 ), + new FieldElement( -12663563,-2181719,1168162,-3804809,26747877,-14138091,10609330,12694420,33473243,-13382104 ), + new FieldElement( 33184999,11180355,15832085,-11385430,-1633671,225884,15089336,-11023903,-6135662,14480053 ) + ), + new GroupElementPreComp( + new FieldElement( 31308717,-5619998,31030840,-1897099,15674547,-6582883,5496208,13685227,27595050,8737275 ), + new FieldElement( -20318852,-15150239,10933843,-16178022,8335352,-7546022,-31008351,-12610604,26498114,66511 ), + new FieldElement( 22644454,-8761729,-16671776,4884562,-3105614,-13559366,30540766,-4286747,-13327787,-7515095 ) + ), + new GroupElementPreComp( + new FieldElement( -28017847,9834845,18617207,-2681312,-3401956,-13307506,8205540,13585437,-17127465,15115439 ), + new FieldElement( 23711543,-672915,31206561,-8362711,6164647,-9709987,-33535882,-1426096,8236921,16492939 ), + new FieldElement( -23910559,-13515526,-26299483,-4503841,25005590,-7687270,19574902,10071562,6708380,-6222424 ) + ), + new GroupElementPreComp( + new FieldElement( 2101391,-4930054,19702731,2367575,-15427167,1047675,5301017,9328700,29955601,-11678310 ), + new FieldElement( 3096359,9271816,-21620864,-15521844,-14847996,-7592937,-25892142,-12635595,-9917575,6216608 ), + new FieldElement( -32615849,338663,-25195611,2510422,-29213566,-13820213,24822830,-6146567,-26767480,7525079 ) + ), + new GroupElementPreComp( + new FieldElement( -23066649,-13985623,16133487,-7896178,-3389565,778788,-910336,-2782495,-19386633,11994101 ), + new FieldElement( 21691500,-13624626,-641331,-14367021,3285881,-3483596,-25064666,9718258,-7477437,13381418 ), + new FieldElement( 18445390,-4202236,14979846,11622458,-1727110,-3582980,23111648,-6375247,28535282,15779576 ) + ), + new GroupElementPreComp( + new FieldElement( 30098053,3089662,-9234387,16662135,-21306940,11308411,-14068454,12021730,9955285,-16303356 ), + new FieldElement( 9734894,-14576830,-7473633,-9138735,2060392,11313496,-18426029,9924399,20194861,13380996 ), + new FieldElement( -26378102,-7965207,-22167821,15789297,-18055342,-6168792,-1984914,15707771,26342023,10146099 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -26016874,-219943,21339191,-41388,19745256,-2878700,-29637280,2227040,21612326,-545728 ), + new FieldElement( -13077387,1184228,23562814,-5970442,-20351244,-6348714,25764461,12243797,-20856566,11649658 ), + new FieldElement( -10031494,11262626,27384172,2271902,26947504,-15997771,39944,6114064,33514190,2333242 ) + ), + new GroupElementPreComp( + new FieldElement( -21433588,-12421821,8119782,7219913,-21830522,-9016134,-6679750,-12670638,24350578,-13450001 ), + new FieldElement( -4116307,-11271533,-23886186,4843615,-30088339,690623,-31536088,-10406836,8317860,12352766 ), + new FieldElement( 18200138,-14475911,-33087759,-2696619,-23702521,-9102511,-23552096,-2287550,20712163,6719373 ) + ), + new GroupElementPreComp( + new FieldElement( 26656208,6075253,-7858556,1886072,-28344043,4262326,11117530,-3763210,26224235,-3297458 ), + new FieldElement( -17168938,-14854097,-3395676,-16369877,-19954045,14050420,21728352,9493610,18620611,-16428628 ), + new FieldElement( -13323321,13325349,11432106,5964811,18609221,6062965,-5269471,-9725556,-30701573,-16479657 ) + ), + new GroupElementPreComp( + new FieldElement( -23860538,-11233159,26961357,1640861,-32413112,-16737940,12248509,-5240639,13735342,1934062 ), + new FieldElement( 25089769,6742589,17081145,-13406266,21909293,-16067981,-15136294,-3765346,-21277997,5473616 ), + new FieldElement( 31883677,-7961101,1083432,-11572403,22828471,13290673,-7125085,12469656,29111212,-5451014 ) + ), + new GroupElementPreComp( + new FieldElement( 24244947,-15050407,-26262976,2791540,-14997599,16666678,24367466,6388839,-10295587,452383 ), + new FieldElement( -25640782,-3417841,5217916,16224624,19987036,-4082269,-24236251,-5915248,15766062,8407814 ), + new FieldElement( -20406999,13990231,15495425,16395525,5377168,15166495,-8917023,-4388953,-8067909,2276718 ) + ), + new GroupElementPreComp( + new FieldElement( 30157918,12924066,-17712050,9245753,19895028,3368142,-23827587,5096219,22740376,-7303417 ), + new FieldElement( 2041139,-14256350,7783687,13876377,-25946985,-13352459,24051124,13742383,-15637599,13295222 ), + new FieldElement( 33338237,-8505733,12532113,7977527,9106186,-1715251,-17720195,-4612972,-4451357,-14669444 ) + ), + new GroupElementPreComp( + new FieldElement( -20045281,5454097,-14346548,6447146,28862071,1883651,-2469266,-4141880,7770569,9620597 ), + new FieldElement( 23208068,7979712,33071466,8149229,1758231,-10834995,30945528,-1694323,-33502340,-14767970 ), + new FieldElement( 1439958,-16270480,-1079989,-793782,4625402,10647766,-5043801,1220118,30494170,-11440799 ) + ), + new GroupElementPreComp( + new FieldElement( -5037580,-13028295,-2970559,-3061767,15640974,-6701666,-26739026,926050,-1684339,-13333647 ), + new FieldElement( 13908495,-3549272,30919928,-6273825,-21521863,7989039,9021034,9078865,3353509,4033511 ), + new FieldElement( -29663431,-15113610,32259991,-344482,24295849,-12912123,23161163,8839127,27485041,7356032 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 9661027,705443,11980065,-5370154,-1628543,14661173,-6346142,2625015,28431036,-16771834 ), + new FieldElement( -23839233,-8311415,-25945511,7480958,-17681669,-8354183,-22545972,14150565,15970762,4099461 ), + new FieldElement( 29262576,16756590,26350592,-8793563,8529671,-11208050,13617293,-9937143,11465739,8317062 ) + ), + new GroupElementPreComp( + new FieldElement( -25493081,-6962928,32500200,-9419051,-23038724,-2302222,14898637,3848455,20969334,-5157516 ), + new FieldElement( -20384450,-14347713,-18336405,13884722,-33039454,2842114,-21610826,-3649888,11177095,14989547 ), + new FieldElement( -24496721,-11716016,16959896,2278463,12066309,10137771,13515641,2581286,-28487508,9930240 ) + ), + new GroupElementPreComp( + new FieldElement( -17751622,-2097826,16544300,-13009300,-15914807,-14949081,18345767,-13403753,16291481,-5314038 ), + new FieldElement( -33229194,2553288,32678213,9875984,8534129,6889387,-9676774,6957617,4368891,9788741 ), + new FieldElement( 16660756,7281060,-10830758,12911820,20108584,-8101676,-21722536,-8613148,16250552,-11111103 ) + ), + new GroupElementPreComp( + new FieldElement( -19765507,2390526,-16551031,14161980,1905286,6414907,4689584,10604807,-30190403,4782747 ), + new FieldElement( -1354539,14736941,-7367442,-13292886,7710542,-14155590,-9981571,4383045,22546403,437323 ), + new FieldElement( 31665577,-12180464,-16186830,1491339,-18368625,3294682,27343084,2786261,-30633590,-14097016 ) + ), + new GroupElementPreComp( + new FieldElement( -14467279,-683715,-33374107,7448552,19294360,14334329,-19690631,2355319,-19284671,-6114373 ), + new FieldElement( 15121312,-15796162,6377020,-6031361,-10798111,-12957845,18952177,15496498,-29380133,11754228 ), + new FieldElement( -2637277,-13483075,8488727,-14303896,12728761,-1622493,7141596,11724556,22761615,-10134141 ) + ), + new GroupElementPreComp( + new FieldElement( 16918416,11729663,-18083579,3022987,-31015732,-13339659,-28741185,-12227393,32851222,11717399 ), + new FieldElement( 11166634,7338049,-6722523,4531520,-29468672,-7302055,31474879,3483633,-1193175,-4030831 ), + new FieldElement( -185635,9921305,31456609,-13536438,-12013818,13348923,33142652,6546660,-19985279,-3948376 ) + ), + new GroupElementPreComp( + new FieldElement( -32460596,11266712,-11197107,-7899103,31703694,3855903,-8537131,-12833048,-30772034,-15486313 ), + new FieldElement( -18006477,12709068,3991746,-6479188,-21491523,-10550425,-31135347,-16049879,10928917,3011958 ), + new FieldElement( -6957757,-15594337,31696059,334240,29576716,14796075,-30831056,-12805180,18008031,10258577 ) + ), + new GroupElementPreComp( + new FieldElement( -22448644,15655569,7018479,-4410003,-30314266,-1201591,-1853465,1367120,25127874,6671743 ), + new FieldElement( 29701166,-14373934,-10878120,9279288,-17568,13127210,21382910,11042292,25838796,4642684 ), + new FieldElement( -20430234,14955537,-24126347,8124619,-5369288,-5990470,30468147,-13900640,18423289,4177476 ) + ) + } + }; + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/base2.cs b/Internal/Ed25519Ref10/base2.cs new file mode 100644 index 00000000..03676e5c --- /dev/null +++ b/Internal/Ed25519Ref10/base2.cs @@ -0,0 +1,50 @@ +using System; + +namespace Chaos.NaCl.Internal.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 ) + ) + }; + } +} diff --git a/Internal/Ed25519Ref10/d.cs b/Internal/Ed25519Ref10/d.cs new file mode 100644 index 00000000..9a9343ab --- /dev/null +++ b/Internal/Ed25519Ref10/d.cs @@ -0,0 +1,9 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + internal static FieldElement d = new FieldElement(-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116); + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/d2.cs b/Internal/Ed25519Ref10/d2.cs new file mode 100644 index 00000000..74793114 --- /dev/null +++ b/Internal/Ed25519Ref10/d2.cs @@ -0,0 +1,9 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + internal static FieldElement d2 = new FieldElement(-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199); + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_0.cs b/Internal/Ed25519Ref10/fe_0.cs new file mode 100644 index 00000000..f2075326 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_0.cs @@ -0,0 +1,12 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + public static void fe_0(out FieldElement h) + { + h = default(FieldElement); + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_1.cs b/Internal/Ed25519Ref10/fe_1.cs new file mode 100644 index 00000000..822ea59a --- /dev/null +++ b/Internal/Ed25519Ref10/fe_1.cs @@ -0,0 +1,13 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + public static void fe_1(out FieldElement h) + { + h = default(FieldElement); + h.x0 = 1; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_add.cs b/Internal/Ed25519Ref10/fe_add.cs new file mode 100644 index 00000000..a5c2fb71 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_add.cs @@ -0,0 +1,63 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 h0 = f0 + g0; + Int32 h1 = f1 + g1; + Int32 h2 = f2 + g2; + Int32 h3 = f3 + g3; + Int32 h4 = f4 + g4; + Int32 h5 = f5 + g5; + Int32 h6 = f6 + g6; + Int32 h7 = f7 + g7; + Int32 h8 = f8 + g8; + Int32 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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_cmov.cs b/Internal/Ed25519Ref10/fe_cmov.cs new file mode 100644 index 00000000..039905b0 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_cmov.cs @@ -0,0 +1,70 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 x0 = f0 ^ g0; + Int32 x1 = f1 ^ g1; + Int32 x2 = f2 ^ g2; + Int32 x3 = f3 ^ g3; + Int32 x4 = f4 ^ g4; + Int32 x5 = f5 ^ g5; + Int32 x6 = f6 ^ g6; + Int32 x7 = f7 ^ g7; + Int32 x8 = f8 ^ g8; + Int32 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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_cswap.cs b/Internal/Ed25519Ref10/fe_cswap.cs new file mode 100644 index 00000000..b28bf20d --- /dev/null +++ b/Internal/Ed25519Ref10/fe_cswap.cs @@ -0,0 +1,78 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 x0 = f0 ^ g0; + Int32 x1 = f1 ^ g1; + Int32 x2 = f2 ^ g2; + Int32 x3 = f3 ^ g3; + Int32 x4 = f4 ^ g4; + Int32 x5 = f5 ^ g5; + Int32 x6 = f6 ^ g6; + Int32 x7 = f7 ^ g7; + Int32 x8 = f8 ^ g8; + Int32 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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_frombytes.cs b/Internal/Ed25519Ref10/fe_frombytes.cs new file mode 100644 index 00000000..49afb183 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_frombytes.cs @@ -0,0 +1,122 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + private static Int64 load_3(byte[] data, int offset) + { + uint result; + result = (uint)data[offset + 0]; + result |= (uint)data[offset + 1] << 8; + result |= (uint)data[offset + 2] << 16; + return (Int64)(UInt64)result; + } + + private static Int64 load_4(byte[] data, int offset) + { + uint result; + result = (uint)data[offset + 0]; + result |= (uint)data[offset + 1] << 8; + result |= (uint)data[offset + 2] << 16; + result |= (uint)data[offset + 3] << 24; + return (Int64)(UInt64)result; + } + + // Ignores top bit of h. + internal static void fe_frombytes(out FieldElement h, byte[] data, int offset) + { + Int64 h0 = load_4(data, offset); + Int64 h1 = load_3(data, offset + 4) << 6; + Int64 h2 = load_3(data, offset + 7) << 5; + Int64 h3 = load_3(data, offset + 10) << 3; + Int64 h4 = load_3(data, offset + 13) << 2; + Int64 h5 = load_4(data, offset + 16); + Int64 h6 = load_3(data, offset + 20) << 7; + Int64 h7 = load_3(data, offset + 23) << 5; + Int64 h8 = load_3(data, offset + 26) << 4; + Int64 h9 = (load_3(data, offset + 29) & 8388607) << 2; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (Int64)(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) + { + Int64 h0 = load_4(data, offset); + Int64 h1 = load_3(data, offset + 4) << 6; + Int64 h2 = load_3(data, offset + 7) << 5; + Int64 h3 = load_3(data, offset + 10) << 3; + Int64 h4 = load_3(data, offset + 13) << 2; + Int64 h5 = load_4(data, offset + 16); + Int64 h6 = load_3(data, offset + 20) << 7; + Int64 h7 = load_3(data, offset + 23) << 5; + Int64 h8 = load_3(data, offset + 26) << 4; + Int64 h9 = load_3(data, offset + 29) << 2; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (Int64)(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; + } + } +} diff --git a/Internal/Ed25519Ref10/fe_invert.cs b/Internal/Ed25519Ref10/fe_invert.cs new file mode 100644 index 00000000..697fcc4b --- /dev/null +++ b/Internal/Ed25519Ref10/fe_invert.cs @@ -0,0 +1,179 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + internal static void fe_invert(out FieldElement result, ref FieldElement z) + { + FieldElement t0; + FieldElement t1; + FieldElement t2; + FieldElement t3; + int i; + + /* qhasm: fe z1 */ + + /* qhasm: fe z2 */ + + /* qhasm: fe z8 */ + + /* qhasm: fe z9 */ + + /* qhasm: fe z11 */ + + /* qhasm: fe z22 */ + + /* qhasm: fe z_5_0 */ + + /* qhasm: fe z_10_5 */ + + /* qhasm: fe z_10_0 */ + + /* qhasm: fe z_20_10 */ + + /* qhasm: fe z_20_0 */ + + /* qhasm: fe z_40_20 */ + + /* qhasm: fe z_40_0 */ + + /* qhasm: fe z_50_10 */ + + /* qhasm: fe z_50_0 */ + + /* qhasm: fe z_100_50 */ + + /* qhasm: fe z_100_0 */ + + /* qhasm: fe z_200_100 */ + + /* qhasm: fe z_200_0 */ + + /* qhasm: fe z_250_50 */ + + /* qhasm: fe z_250_0 */ + + /* qhasm: fe z_255_5 */ + + /* qhasm: fe z_255_21 */ + + /* qhasm: enter pow225521 */ + + /* qhasm: z2 = z1^2^1 */ + /* asm 1: fe_sq(>z2=fe#1,z2=fe#1,>z2=fe#1); */ + /* asm 2: fe_sq(>z2=t0,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,z8=fe#2,>z8=fe#2); */ + /* asm 2: fe_sq(>z8=t1,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,z9=t1,z11=fe#1,z11=t0,z22=fe#3,z22=fe#3,>z22=fe#3); */ + /* asm 2: fe_sq(>z22=t2,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,z_5_0=t1,z_10_5=fe#3,z_10_5=fe#3,>z_10_5=fe#3); */ + /* asm 2: fe_sq(>z_10_5=t2,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_0=t1,z_20_10=fe#3,z_20_10=fe#3,>z_20_10=fe#3); */ + /* asm 2: fe_sq(>z_20_10=t2,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_0=t2,z_40_20=fe#4,z_40_20=fe#4,>z_40_20=fe#4); */ + /* asm 2: fe_sq(>z_40_20=t3,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_0=t2,z_50_10=fe#3,z_50_10=fe#3,>z_50_10=fe#3); */ + /* asm 2: fe_sq(>z_50_10=t2,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_0=t1,z_100_50=fe#3,z_100_50=fe#3,>z_100_50=fe#3); */ + /* asm 2: fe_sq(>z_100_50=t2,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_0=t2,z_200_100=fe#4,z_200_100=fe#4,>z_200_100=fe#4); */ + /* asm 2: fe_sq(>z_200_100=t3,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_0=t2,z_250_50=fe#3,z_250_50=fe#3,>z_250_50=fe#3); */ + /* asm 2: fe_sq(>z_250_50=t2,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_0=t1,z_255_5=fe#2,z_255_5=fe#2,>z_255_5=fe#2); */ + /* asm 2: fe_sq(>z_255_5=t1,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_21=out,> 31) ^ 1); + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_mul.cs b/Internal/Ed25519Ref10/fe_mul.cs new file mode 100644 index 00000000..d3c755b8 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_mul.cs @@ -0,0 +1,258 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 g1_19 = 19 * g1; /* 1.959375*2^29 */ + Int32 g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + Int32 g3_19 = 19 * g3; + Int32 g4_19 = 19 * g4; + Int32 g5_19 = 19 * g5; + Int32 g6_19 = 19 * g6; + Int32 g7_19 = 19 * g7; + Int32 g8_19 = 19 * g8; + Int32 g9_19 = 19 * g9; + Int32 f1_2 = 2 * f1; + Int32 f3_2 = 2 * f3; + Int32 f5_2 = 2 * f5; + Int32 f7_2 = 2 * f7; + Int32 f9_2 = 2 * f9; + Int64 f0g0 = f0 * (Int64)g0; + Int64 f0g1 = f0 * (Int64)g1; + Int64 f0g2 = f0 * (Int64)g2; + Int64 f0g3 = f0 * (Int64)g3; + Int64 f0g4 = f0 * (Int64)g4; + Int64 f0g5 = f0 * (Int64)g5; + Int64 f0g6 = f0 * (Int64)g6; + Int64 f0g7 = f0 * (Int64)g7; + Int64 f0g8 = f0 * (Int64)g8; + Int64 f0g9 = f0 * (Int64)g9; + Int64 f1g0 = f1 * (Int64)g0; + Int64 f1g1_2 = f1_2 * (Int64)g1; + Int64 f1g2 = f1 * (Int64)g2; + Int64 f1g3_2 = f1_2 * (Int64)g3; + Int64 f1g4 = f1 * (Int64)g4; + Int64 f1g5_2 = f1_2 * (Int64)g5; + Int64 f1g6 = f1 * (Int64)g6; + Int64 f1g7_2 = f1_2 * (Int64)g7; + Int64 f1g8 = f1 * (Int64)g8; + Int64 f1g9_38 = f1_2 * (Int64)g9_19; + Int64 f2g0 = f2 * (Int64)g0; + Int64 f2g1 = f2 * (Int64)g1; + Int64 f2g2 = f2 * (Int64)g2; + Int64 f2g3 = f2 * (Int64)g3; + Int64 f2g4 = f2 * (Int64)g4; + Int64 f2g5 = f2 * (Int64)g5; + Int64 f2g6 = f2 * (Int64)g6; + Int64 f2g7 = f2 * (Int64)g7; + Int64 f2g8_19 = f2 * (Int64)g8_19; + Int64 f2g9_19 = f2 * (Int64)g9_19; + Int64 f3g0 = f3 * (Int64)g0; + Int64 f3g1_2 = f3_2 * (Int64)g1; + Int64 f3g2 = f3 * (Int64)g2; + Int64 f3g3_2 = f3_2 * (Int64)g3; + Int64 f3g4 = f3 * (Int64)g4; + Int64 f3g5_2 = f3_2 * (Int64)g5; + Int64 f3g6 = f3 * (Int64)g6; + Int64 f3g7_38 = f3_2 * (Int64)g7_19; + Int64 f3g8_19 = f3 * (Int64)g8_19; + Int64 f3g9_38 = f3_2 * (Int64)g9_19; + Int64 f4g0 = f4 * (Int64)g0; + Int64 f4g1 = f4 * (Int64)g1; + Int64 f4g2 = f4 * (Int64)g2; + Int64 f4g3 = f4 * (Int64)g3; + Int64 f4g4 = f4 * (Int64)g4; + Int64 f4g5 = f4 * (Int64)g5; + Int64 f4g6_19 = f4 * (Int64)g6_19; + Int64 f4g7_19 = f4 * (Int64)g7_19; + Int64 f4g8_19 = f4 * (Int64)g8_19; + Int64 f4g9_19 = f4 * (Int64)g9_19; + Int64 f5g0 = f5 * (Int64)g0; + Int64 f5g1_2 = f5_2 * (Int64)g1; + Int64 f5g2 = f5 * (Int64)g2; + Int64 f5g3_2 = f5_2 * (Int64)g3; + Int64 f5g4 = f5 * (Int64)g4; + Int64 f5g5_38 = f5_2 * (Int64)g5_19; + Int64 f5g6_19 = f5 * (Int64)g6_19; + Int64 f5g7_38 = f5_2 * (Int64)g7_19; + Int64 f5g8_19 = f5 * (Int64)g8_19; + Int64 f5g9_38 = f5_2 * (Int64)g9_19; + Int64 f6g0 = f6 * (Int64)g0; + Int64 f6g1 = f6 * (Int64)g1; + Int64 f6g2 = f6 * (Int64)g2; + Int64 f6g3 = f6 * (Int64)g3; + Int64 f6g4_19 = f6 * (Int64)g4_19; + Int64 f6g5_19 = f6 * (Int64)g5_19; + Int64 f6g6_19 = f6 * (Int64)g6_19; + Int64 f6g7_19 = f6 * (Int64)g7_19; + Int64 f6g8_19 = f6 * (Int64)g8_19; + Int64 f6g9_19 = f6 * (Int64)g9_19; + Int64 f7g0 = f7 * (Int64)g0; + Int64 f7g1_2 = f7_2 * (Int64)g1; + Int64 f7g2 = f7 * (Int64)g2; + Int64 f7g3_38 = f7_2 * (Int64)g3_19; + Int64 f7g4_19 = f7 * (Int64)g4_19; + Int64 f7g5_38 = f7_2 * (Int64)g5_19; + Int64 f7g6_19 = f7 * (Int64)g6_19; + Int64 f7g7_38 = f7_2 * (Int64)g7_19; + Int64 f7g8_19 = f7 * (Int64)g8_19; + Int64 f7g9_38 = f7_2 * (Int64)g9_19; + Int64 f8g0 = f8 * (Int64)g0; + Int64 f8g1 = f8 * (Int64)g1; + Int64 f8g2_19 = f8 * (Int64)g2_19; + Int64 f8g3_19 = f8 * (Int64)g3_19; + Int64 f8g4_19 = f8 * (Int64)g4_19; + Int64 f8g5_19 = f8 * (Int64)g5_19; + Int64 f8g6_19 = f8 * (Int64)g6_19; + Int64 f8g7_19 = f8 * (Int64)g7_19; + Int64 f8g8_19 = f8 * (Int64)g8_19; + Int64 f8g9_19 = f8 * (Int64)g9_19; + Int64 f9g0 = f9 * (Int64)g0; + Int64 f9g1_38 = f9_2 * (Int64)g1_19; + Int64 f9g2_19 = f9 * (Int64)g2_19; + Int64 f9g3_38 = f9_2 * (Int64)g3_19; + Int64 f9g4_19 = f9 * (Int64)g4_19; + Int64 f9g5_38 = f9_2 * (Int64)g5_19; + Int64 f9g6_19 = f9 * (Int64)g6_19; + Int64 f9g7_38 = f9_2 * (Int64)g7_19; + Int64 f9g8_19 = f9 * (Int64)g8_19; + Int64 f9g9_38 = f9_2 * (Int64)g9_19; + Int64 h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + Int64 h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; + Int64 h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; + Int64 h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; + Int64 h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; + Int64 h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; + Int64 h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; + Int64 h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; + Int64 h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; + Int64 h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 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 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (Int64)(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 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (Int64)(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 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (Int64)(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 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (Int64)(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 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (Int64)(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 + (Int64)(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 + (Int64)(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 = (Int32)h0; + h.x1 = (Int32)h1; + h.x2 = (Int32)h2; + h.x3 = (Int32)h3; + h.x4 = (Int32)h4; + h.x5 = (Int32)h5; + h.x6 = (Int32)h6; + h.x7 = (Int32)h7; + h.x8 = (Int32)h8; + h.x9 = (Int32)h9; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_mul121666.cs b/Internal/Ed25519Ref10/fe_mul121666.cs new file mode 100644 index 00000000..91325273 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_mul121666.cs @@ -0,0 +1,76 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int64 h0 = f0 * (Int64)121666; + Int64 h1 = f1 * (Int64)121666; + Int64 h2 = f2 * (Int64)121666; + Int64 h3 = f3 * (Int64)121666; + Int64 h4 = f4 * (Int64)121666; + Int64 h5 = f5 * (Int64)121666; + Int64 h6 = f6 * (Int64)121666; + Int64 h7 = f7 * (Int64)121666; + Int64 h8 = f8 * (Int64)121666; + Int64 h9 = f9 * (Int64)121666; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (Int64)(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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_neg.cs b/Internal/Ed25519Ref10/fe_neg.cs new file mode 100644 index 00000000..64665ec5 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_neg.cs @@ -0,0 +1,50 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 h0 = -f0; + Int32 h1 = -f1; + Int32 h2 = -f2; + Int32 h3 = -f3; + Int32 h4 = -f4; + Int32 h5 = -f5; + Int32 h6 = -f6; + Int32 h7 = -f7; + Int32 h8 = -f8; + Int32 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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_pow22523.cs b/Internal/Ed25519Ref10/fe_pow22523.cs new file mode 100644 index 00000000..5018f6dc --- /dev/null +++ b/Internal/Ed25519Ref10/fe_pow22523.cs @@ -0,0 +1,175 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + internal static void fe_pow22523(out FieldElement result, ref FieldElement z) + { + FieldElement t0; + FieldElement t1; + FieldElement t2; + int i; + + /* qhasm: fe z1 */ + + /* qhasm: fe z2 */ + + /* qhasm: fe z8 */ + + /* qhasm: fe z9 */ + + /* qhasm: fe z11 */ + + /* qhasm: fe z22 */ + + /* qhasm: fe z_5_0 */ + + /* qhasm: fe z_10_5 */ + + /* qhasm: fe z_10_0 */ + + /* qhasm: fe z_20_10 */ + + /* qhasm: fe z_20_0 */ + + /* qhasm: fe z_40_20 */ + + /* qhasm: fe z_40_0 */ + + /* qhasm: fe z_50_10 */ + + /* qhasm: fe z_50_0 */ + + /* qhasm: fe z_100_50 */ + + /* qhasm: fe z_100_0 */ + + /* qhasm: fe z_200_100 */ + + /* qhasm: fe z_200_0 */ + + /* qhasm: fe z_250_50 */ + + /* qhasm: fe z_250_0 */ + + /* qhasm: fe z_252_2 */ + + /* qhasm: fe z_252_3 */ + + /* qhasm: enter pow22523 */ + + /* qhasm: z2 = z1^2^1 */ + /* asm 1: fe_sq(>z2=fe#1,z2=fe#1,>z2=fe#1); */ + /* asm 2: fe_sq(>z2=t0,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,z8=fe#2,>z8=fe#2); */ + /* asm 2: fe_sq(>z8=t1,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,z9=t1,z11=fe#1,z11=t0,z22=fe#1,z22=fe#1,>z22=fe#1); */ + /* asm 2: fe_sq(>z22=t0,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,z_5_0=t0,z_10_5=fe#2,z_10_5=fe#2,>z_10_5=fe#2); */ + /* asm 2: fe_sq(>z_10_5=t1,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_0=t0,z_20_10=fe#2,z_20_10=fe#2,>z_20_10=fe#2); */ + /* asm 2: fe_sq(>z_20_10=t1,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_0=t1,z_40_20=fe#3,z_40_20=fe#3,>z_40_20=fe#3); */ + /* asm 2: fe_sq(>z_40_20=t2,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_0=t1,z_50_10=fe#2,z_50_10=fe#2,>z_50_10=fe#2); */ + /* asm 2: fe_sq(>z_50_10=t1,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_0=t0,z_100_50=fe#2,z_100_50=fe#2,>z_100_50=fe#2); */ + /* asm 2: fe_sq(>z_100_50=t1,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_0=t1,z_200_100=fe#3,z_200_100=fe#3,>z_200_100=fe#3); */ + /* asm 2: fe_sq(>z_200_100=t2,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_0=t1,z_250_50=fe#2,z_250_50=fe#2,>z_250_50=fe#2); */ + /* asm 2: fe_sq(>z_250_50=t1,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_0=t0,z_252_2=fe#1,z_252_2=fe#1,>z_252_2=fe#1); */ + /* asm 2: fe_sq(>z_252_2=t0,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_3=out,> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h.x0 = (Int32)h0; + h.x1 = (Int32)h1; + h.x2 = (Int32)h2; + h.x3 = (Int32)h3; + h.x4 = (Int32)h4; + h.x5 = (Int32)h5; + h.x6 = (Int32)h6; + h.x7 = (Int32)h7; + h.x8 = (Int32)h8; + h.x9 = (Int32)h9; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_sq2.cs b/Internal/Ed25519Ref10/fe_sq2.cs new file mode 100644 index 00000000..40913bb7 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_sq2.cs @@ -0,0 +1,164 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 f0_2 = 2 * f0; + Int32 f1_2 = 2 * f1; + Int32 f2_2 = 2 * f2; + Int32 f3_2 = 2 * f3; + Int32 f4_2 = 2 * f4; + Int32 f5_2 = 2 * f5; + Int32 f6_2 = 2 * f6; + Int32 f7_2 = 2 * f7; + Int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ + Int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ + Int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ + Int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ + Int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ + Int64 f0f0 = f0 * (Int64)f0; + Int64 f0f1_2 = f0_2 * (Int64)f1; + Int64 f0f2_2 = f0_2 * (Int64)f2; + Int64 f0f3_2 = f0_2 * (Int64)f3; + Int64 f0f4_2 = f0_2 * (Int64)f4; + Int64 f0f5_2 = f0_2 * (Int64)f5; + Int64 f0f6_2 = f0_2 * (Int64)f6; + Int64 f0f7_2 = f0_2 * (Int64)f7; + Int64 f0f8_2 = f0_2 * (Int64)f8; + Int64 f0f9_2 = f0_2 * (Int64)f9; + Int64 f1f1_2 = f1_2 * (Int64)f1; + Int64 f1f2_2 = f1_2 * (Int64)f2; + Int64 f1f3_4 = f1_2 * (Int64)f3_2; + Int64 f1f4_2 = f1_2 * (Int64)f4; + Int64 f1f5_4 = f1_2 * (Int64)f5_2; + Int64 f1f6_2 = f1_2 * (Int64)f6; + Int64 f1f7_4 = f1_2 * (Int64)f7_2; + Int64 f1f8_2 = f1_2 * (Int64)f8; + Int64 f1f9_76 = f1_2 * (Int64)f9_38; + Int64 f2f2 = f2 * (Int64)f2; + Int64 f2f3_2 = f2_2 * (Int64)f3; + Int64 f2f4_2 = f2_2 * (Int64)f4; + Int64 f2f5_2 = f2_2 * (Int64)f5; + Int64 f2f6_2 = f2_2 * (Int64)f6; + Int64 f2f7_2 = f2_2 * (Int64)f7; + Int64 f2f8_38 = f2_2 * (Int64)f8_19; + Int64 f2f9_38 = f2 * (Int64)f9_38; + Int64 f3f3_2 = f3_2 * (Int64)f3; + Int64 f3f4_2 = f3_2 * (Int64)f4; + Int64 f3f5_4 = f3_2 * (Int64)f5_2; + Int64 f3f6_2 = f3_2 * (Int64)f6; + Int64 f3f7_76 = f3_2 * (Int64)f7_38; + Int64 f3f8_38 = f3_2 * (Int64)f8_19; + Int64 f3f9_76 = f3_2 * (Int64)f9_38; + Int64 f4f4 = f4 * (Int64)f4; + Int64 f4f5_2 = f4_2 * (Int64)f5; + Int64 f4f6_38 = f4_2 * (Int64)f6_19; + Int64 f4f7_38 = f4 * (Int64)f7_38; + Int64 f4f8_38 = f4_2 * (Int64)f8_19; + Int64 f4f9_38 = f4 * (Int64)f9_38; + Int64 f5f5_38 = f5 * (Int64)f5_38; + Int64 f5f6_38 = f5_2 * (Int64)f6_19; + Int64 f5f7_76 = f5_2 * (Int64)f7_38; + Int64 f5f8_38 = f5_2 * (Int64)f8_19; + Int64 f5f9_76 = f5_2 * (Int64)f9_38; + Int64 f6f6_19 = f6 * (Int64)f6_19; + Int64 f6f7_38 = f6 * (Int64)f7_38; + Int64 f6f8_38 = f6_2 * (Int64)f8_19; + Int64 f6f9_38 = f6 * (Int64)f9_38; + Int64 f7f7_38 = f7 * (Int64)f7_38; + Int64 f7f8_38 = f7_2 * (Int64)f8_19; + Int64 f7f9_76 = f7_2 * (Int64)f9_38; + Int64 f8f8_19 = f8 * (Int64)f8_19; + Int64 f8f9_38 = f8 * (Int64)f9_38; + Int64 f9f9_38 = f9 * (Int64)f9_38; + Int64 h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + Int64 h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + Int64 h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + Int64 h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + Int64 h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + Int64 h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + Int64 h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + Int64 h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + Int64 h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + Int64 h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h.x0 = (Int32)h0; + h.x1 = (Int32)h1; + h.x2 = (Int32)h2; + h.x3 = (Int32)h3; + h.x4 = (Int32)h4; + h.x5 = (Int32)h5; + h.x6 = (Int32)h6; + h.x7 = (Int32)h7; + h.x8 = (Int32)h8; + h.x9 = (Int32)h9; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_sub.cs b/Internal/Ed25519Ref10/fe_sub.cs new file mode 100644 index 00000000..4de0e6c6 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_sub.cs @@ -0,0 +1,63 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 h0 = f0 - g0; + Int32 h1 = f1 - g1; + Int32 h2 = f2 - g2; + Int32 h3 = f3 - g3; + Int32 h4 = f4 - g4; + Int32 h5 = f5 - g5; + Int32 h6 = f6 - g6; + Int32 h7 = f7 - g7; + Int32 h8 = f8 - g8; + Int32 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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/fe_tobytes.cs b/Internal/Ed25519Ref10/fe_tobytes.cs new file mode 100644 index 00000000..9a36a8f8 --- /dev/null +++ b/Internal/Ed25519Ref10/fe_tobytes.cs @@ -0,0 +1,154 @@ +using System; + +namespace Chaos.NaCl.Internal.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> 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) + { + Int32 h0 = h.x0; + Int32 h1 = h.x1; + Int32 h2 = h.x2; + Int32 h3 = h.x3; + Int32 h4 = h.x4; + Int32 h5 = h.x5; + Int32 h6 = h.x6; + Int32 h7 = h.x7; + Int32 h8 = h.x8; + Int32 h9 = h.x9; + Int32 q; + Int32 carry0; + Int32 carry1; + Int32 carry2; + Int32 carry3; + Int32 carry4; + Int32 carry5; + Int32 carry6; + Int32 carry7; + Int32 carry8; + Int32 carry9; + + q = (19 * h9 + (((Int32)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. */ + + carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; + carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; + carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; + carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; + carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; + carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; + carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; + carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; + carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; + 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; + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/ge_add.cs b/Internal/Ed25519Ref10/ge_add.cs new file mode 100644 index 00000000..f80a8176 --- /dev/null +++ b/Internal/Ed25519Ref10/ge_add.cs @@ -0,0 +1,113 @@ +using System; + +namespace Chaos.NaCl.Internal.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: enter GroupElementadd */ + + /* qhasm: fe X1 */ + + /* qhasm: fe Y1 */ + + /* qhasm: fe Z1 */ + + /* qhasm: fe Z2 */ + + /* qhasm: fe T1 */ + + /* qhasm: fe ZZ */ + + /* qhasm: fe YpX2 */ + + /* qhasm: fe YmX2 */ + + /* qhasm: fe T2d2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Y3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe T3 */ + + /* qhasm: fe YpX1 */ + + /* qhasm: fe YmX1 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: YpX1 = Y1+X1 */ + /* asm 1: fe_add(>YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,ZZ=fe#1,ZZ=r.X,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,> 3] >> (i & 7))); + + for (i = 0; i < 256; ++i) + if (r[i] != 0) + { + for (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 (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); + } + } + + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/ge_frombytes.cs b/Internal/Ed25519Ref10/ge_frombytes.cs new file mode 100644 index 00000000..6ec8dc49 --- /dev/null +++ b/Internal/Ed25519Ref10/ge_frombytes.cs @@ -0,0 +1,54 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class GroupOperations + { + public static int ge_frombytes_negate_vartime(out GroupElementP3 h, byte[] data, int offset) + { + FieldElement u; + FieldElement v; + FieldElement v3; + FieldElement vxx; + FieldElement 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; + } + + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/ge_madd.cs b/Internal/Ed25519Ref10/ge_madd.cs new file mode 100644 index 00000000..de80fa61 --- /dev/null +++ b/Internal/Ed25519Ref10/ge_madd.cs @@ -0,0 +1,105 @@ +using System; + +namespace Chaos.NaCl.Internal.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: enter ge_madd */ + + /* qhasm: fe X1 */ + + /* qhasm: fe Y1 */ + + /* qhasm: fe Z1 */ + + /* qhasm: fe T1 */ + + /* qhasm: fe ypx2 */ + + /* qhasm: fe ymx2 */ + + /* qhasm: fe xy2d2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Y3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe T3 */ + + /* qhasm: fe YpX1 */ + + /* qhasm: fe YmX1 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: YpX1 = Y1+X1 */ + /* asm 1: fe_add(>YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,XX=fe#1,XX=r.X,YY=fe#3,YY=r.Z,B=fe#4,B=r.T,A=fe#2,A=r.Y,AA=fe#5,AA=t0,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,X3=fe#1,X3=r.X,T3=fe#4,T3=r.T,>= 31; /* 1: yes; 0: no */ + return (byte)y; + } + + static byte negative(sbyte b) + { + ulong x = unchecked((ulong)(long)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; + byte bnegative = negative(b); + byte 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 + sbyte[] e = new sbyte[64]; + sbyte carry; + GroupElementP1P1 r; + GroupElementP2 s; + GroupElementPreComp t; + int i; + + for (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 (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 (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 (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); + } + } + + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/ge_sub.cs b/Internal/Ed25519Ref10/ge_sub.cs new file mode 100644 index 00000000..0965b53f --- /dev/null +++ b/Internal/Ed25519Ref10/ge_sub.cs @@ -0,0 +1,114 @@ +using System; + +namespace Chaos.NaCl.Internal.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: enter ge_sub */ + + /* qhasm: fe X1 */ + + /* qhasm: fe Y1 */ + + /* qhasm: fe Z1 */ + + /* qhasm: fe Z2 */ + + /* qhasm: fe T1 */ + + /* qhasm: fe ZZ */ + + /* qhasm: fe YpX2 */ + + /* qhasm: fe YmX2 */ + + /* qhasm: fe T2d2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Y3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe T3 */ + + /* qhasm: fe YpX1 */ + + /* qhasm: fe YmX1 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: YpX1 = Y1+X1 */ + /* asm 1: fe_add(>YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,ZZ=fe#1,ZZ=r.X,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,> 5); + Int64 a2 = 2097151 & (load_3(a, 5) >> 2); + Int64 a3 = 2097151 & (load_4(a, 7) >> 7); + Int64 a4 = 2097151 & (load_4(a, 10) >> 4); + Int64 a5 = 2097151 & (load_3(a, 13) >> 1); + Int64 a6 = 2097151 & (load_4(a, 15) >> 6); + Int64 a7 = 2097151 & (load_3(a, 18) >> 3); + Int64 a8 = 2097151 & load_3(a, 21); + Int64 a9 = 2097151 & (load_4(a, 23) >> 5); + Int64 a10 = 2097151 & (load_3(a, 26) >> 2); + Int64 a11 = (load_4(a, 28) >> 7); + Int64 b0 = 2097151 & load_3(b, 0); + Int64 b1 = 2097151 & (load_4(b, 2) >> 5); + Int64 b2 = 2097151 & (load_3(b, 5) >> 2); + Int64 b3 = 2097151 & (load_4(b, 7) >> 7); + Int64 b4 = 2097151 & (load_4(b, 10) >> 4); + Int64 b5 = 2097151 & (load_3(b, 13) >> 1); + Int64 b6 = 2097151 & (load_4(b, 15) >> 6); + Int64 b7 = 2097151 & (load_3(b, 18) >> 3); + Int64 b8 = 2097151 & load_3(b, 21); + Int64 b9 = 2097151 & (load_4(b, 23) >> 5); + Int64 b10 = 2097151 & (load_3(b, 26) >> 2); + Int64 b11 = (load_4(b, 28) >> 7); + Int64 c0 = 2097151 & load_3(c, 0); + Int64 c1 = 2097151 & (load_4(c, 2) >> 5); + Int64 c2 = 2097151 & (load_3(c, 5) >> 2); + Int64 c3 = 2097151 & (load_4(c, 7) >> 7); + Int64 c4 = 2097151 & (load_4(c, 10) >> 4); + Int64 c5 = 2097151 & (load_3(c, 13) >> 1); + Int64 c6 = 2097151 & (load_4(c, 15) >> 6); + Int64 c7 = 2097151 & (load_3(c, 18) >> 3); + Int64 c8 = 2097151 & load_3(c, 21); + Int64 c9 = 2097151 & (load_4(c, 23) >> 5); + Int64 c10 = 2097151 & (load_3(c, 26) >> 2); + Int64 c11 = (load_4(c, 28) >> 7); + Int64 s0; + Int64 s1; + Int64 s2; + Int64 s3; + Int64 s4; + Int64 s5; + Int64 s6; + Int64 s7; + Int64 s8; + Int64 s9; + Int64 s10; + Int64 s11; + Int64 s12; + Int64 s13; + Int64 s14; + Int64 s15; + Int64 s16; + Int64 s17; + Int64 s18; + Int64 s19; + Int64 s20; + Int64 s21; + Int64 s22; + Int64 s23; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + Int64 carry10; + Int64 carry11; + Int64 carry12; + Int64 carry13; + Int64 carry14; + Int64 carry15; + Int64 carry16; + Int64 carry17; + Int64 carry18; + Int64 carry19; + Int64 carry20; + Int64 carry21; + Int64 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); + } + } + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/sc_reduce.cs b/Internal/Ed25519Ref10/sc_reduce.cs new file mode 100644 index 00000000..493bf5a4 --- /dev/null +++ b/Internal/Ed25519Ref10/sc_reduce.cs @@ -0,0 +1,263 @@ +using System; + +namespace Chaos.NaCl.Internal.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) + { + Int64 s0 = 2097151 & load_3(s, 0); + Int64 s1 = 2097151 & (load_4(s, 2) >> 5); + Int64 s2 = 2097151 & (load_3(s, 5) >> 2); + Int64 s3 = 2097151 & (load_4(s, 7) >> 7); + Int64 s4 = 2097151 & (load_4(s, 10) >> 4); + Int64 s5 = 2097151 & (load_3(s, 13) >> 1); + Int64 s6 = 2097151 & (load_4(s, 15) >> 6); + Int64 s7 = 2097151 & (load_3(s, 18) >> 3); + Int64 s8 = 2097151 & load_3(s, 21); + Int64 s9 = 2097151 & (load_4(s, 23) >> 5); + Int64 s10 = 2097151 & (load_3(s, 26) >> 2); + Int64 s11 = 2097151 & (load_4(s, 28) >> 7); + Int64 s12 = 2097151 & (load_4(s, 31) >> 4); + Int64 s13 = 2097151 & (load_3(s, 34) >> 1); + Int64 s14 = 2097151 & (load_4(s, 36) >> 6); + Int64 s15 = 2097151 & (load_3(s, 39) >> 3); + Int64 s16 = 2097151 & load_3(s, 42); + Int64 s17 = 2097151 & (load_4(s, 44) >> 5); + Int64 s18 = 2097151 & (load_3(s, 47) >> 2); + Int64 s19 = 2097151 & (load_4(s, 49) >> 7); + Int64 s20 = 2097151 & (load_4(s, 52) >> 4); + Int64 s21 = 2097151 & (load_3(s, 55) >> 1); + Int64 s22 = 2097151 & (load_4(s, 57) >> 6); + Int64 s23 = (load_4(s, 60) >> 3); + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + Int64 carry10; + Int64 carry11; + Int64 carry12; + Int64 carry13; + Int64 carry14; + Int64 carry15; + Int64 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); + } + } + + } +} \ No newline at end of file diff --git a/Internal/Ed25519Ref10/scalarmult.cs b/Internal/Ed25519Ref10/scalarmult.cs new file mode 100644 index 00000000..b0b12476 --- /dev/null +++ b/Internal/Ed25519Ref10/scalarmult.cs @@ -0,0 +1,205 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + public static class MontgomeryOperations + { + public static void scalarmult( + byte[] q, int qoffset, + byte[] n, int noffset, + byte[] p, int poffset) + { + FieldElement p0; + FieldElement 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 + UInt32 i; + FieldElement x1; + FieldElement x2; + FieldElement z2; + FieldElement x3; + FieldElement z3; + FieldElement tmp0; + FieldElement tmp1; + int pos; + UInt32 swap; + UInt32 b; + + for (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); + + swap = 0; + for (pos = 254; pos >= 0; --pos) + { + 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: fe X2 */ + + /* qhasm: fe Z2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe X4 */ + + /* qhasm: fe Z4 */ + + /* qhasm: fe X5 */ + + /* qhasm: fe Z5 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: fe E */ + + /* qhasm: fe AA */ + + /* qhasm: fe BB */ + + /* qhasm: fe DA */ + + /* qhasm: fe CB */ + + /* qhasm: fe t0 */ + + /* qhasm: fe t1 */ + + /* qhasm: fe t2 */ + + /* qhasm: fe t3 */ + + /* qhasm: fe t4 */ + + /* qhasm: enter ladder */ + + /* qhasm: D = X3-Z3 */ + /* asm 1: fe_sub(>D=fe#5,D=tmp0,B=fe#6,B=tmp1,A=fe#1,A=x2,C=fe#2,C=z2,DA=fe#4,DA=z3,CB=fe#2,CB=z2,BB=fe#5,BB=tmp0,AA=fe#6,AA=tmp1,t0=fe#3,t0=x3,t1=fe#2,t1=z2,X4=fe#1,X4=x2,E=fe#6,E=tmp1,t2=fe#2,t2=z2,t3=fe#4,t3=z3,X5=fe#3,X5=x3,t4=fe#5,t4=tmp0,Z5=fe#4,x1,Z5=z3,x1,Z4=fe#2,Z4=z2, key) + { + UInt32 t0, t1, t2, t3; + UInt32 h0, h1, h2, h3, h4; + UInt32 r0, r1, r2, r3, r4; + UInt32 s1, s2, s3, s4; + UInt32 b, nb; + int j; + UInt64 tt0, tt1, tt2, tt3, tt4; + UInt64 f0, f1, f2, f3; + UInt32 g0, g1, g2, g3, g4; + UInt64 c; + + /* clamp key */ + t0 = key.x0; + t1 = key.x1; + t2 = key.x2; + t3 = key.x3; + + /* precompute multipliers */ + r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; + r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; + r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; + r3 = t2 & 0x3f03fff; t3 >>= 8; + r4 = t3 & 0x00fffff; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + /* init state */ + h0 = 0; + h1 = 0; + h2 = 0; + h3 = 0; + h4 = 0; + + /* full blocks */ + if (mLength < 16) + goto poly1305_donna_atmost15bytes; + + poly1305_donna_16bytes: + mStart += 16; + mLength -= 16; + + t0 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 16); + t1 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 12); + t2 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 8); + t3 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 4); + + //todo: looks like these can be simplified a bit + h0 += t0 & 0x3ffffff; + h1 += (uint)(((((UInt64)t1 << 32) | t0) >> 26) & 0x3ffffff); + h2 += (uint)(((((UInt64)t2 << 32) | t1) >> 20) & 0x3ffffff); + h3 += (uint)(((((UInt64)t3 << 32) | t2) >> 14) & 0x3ffffff); + h4 += (t3 >> 8) | (1 << 24); + + + poly1305_donna_mul: + tt0 = (ulong)h0 * r0 + (ulong)h1 * s4 + (ulong)h2 * s3 + (ulong)h3 * s2 + (ulong)h4 * s1; + tt1 = (ulong)h0 * r1 + (ulong)h1 * r0 + (ulong)h2 * s4 + (ulong)h3 * s3 + (ulong)h4 * s2; + tt2 = (ulong)h0 * r2 + (ulong)h1 * r1 + (ulong)h2 * r0 + (ulong)h3 * s4 + (ulong)h4 * s3; + tt3 = (ulong)h0 * r3 + (ulong)h1 * r2 + (ulong)h2 * r1 + (ulong)h3 * r0 + (ulong)h4 * s4; + tt4 = (ulong)h0 * r4 + (ulong)h1 * r3 + (ulong)h2 * r2 + (ulong)h3 * r1 + (ulong)h4 * r0; + + unchecked + { + h0 = (UInt32)tt0 & 0x3ffffff; c = (tt0 >> 26); + tt1 += c; h1 = (UInt32)tt1 & 0x3ffffff; b = (UInt32)(tt1 >> 26); + tt2 += b; h2 = (UInt32)tt2 & 0x3ffffff; b = (UInt32)(tt2 >> 26); + tt3 += b; h3 = (UInt32)tt3 & 0x3ffffff; b = (UInt32)(tt3 >> 26); + tt4 += b; h4 = (UInt32)tt4 & 0x3ffffff; b = (UInt32)(tt4 >> 26); + } + h0 += b * 5; + + if (mLength >= 16) + goto poly1305_donna_16bytes; + + /* final bytes */ + poly1305_donna_atmost15bytes: + if (mLength == 0) + goto poly1305_donna_finish; + + byte[] mp = new byte[16];//todo remove allocation + + for (j = 0; j < mLength; j++) + mp[j] = m[mStart + j]; + mp[j++] = 1; + for (; j < 16; j++) + mp[j] = 0; + mLength = 0; + + t0 = ByteIntegerConverter.LoadLittleEndian32(mp, 0); + t1 = ByteIntegerConverter.LoadLittleEndian32(mp, 4); + t2 = ByteIntegerConverter.LoadLittleEndian32(mp, 8); + t3 = ByteIntegerConverter.LoadLittleEndian32(mp, 12); + CryptoBytes.Wipe(mp); + + h0 += t0 & 0x3ffffff; + h1 += (uint)(((((UInt64)t1 << 32) | t0) >> 26) & 0x3ffffff); + h2 += (uint)(((((UInt64)t2 << 32) | t1) >> 20) & 0x3ffffff); + h3 += (uint)(((((UInt64)t3 << 32) | t2) >> 14) & 0x3ffffff); + h4 += t3 >> 8; + + goto poly1305_donna_mul; + + poly1305_donna_finish: + b = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += b * 5; + + g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; + g4 = unchecked(h4 + b - (1 << 26)); + + b = (g4 >> 31) - 1; + nb = ~b; + h0 = (h0 & nb) | (g0 & b); + h1 = (h1 & nb) | (g1 & b); + h2 = (h2 & nb) | (g2 & b); + h3 = (h3 & nb) | (g3 & b); + h4 = (h4 & nb) | (g4 & b); + + f0 = ((h0) | (h1 << 26)) + (UInt64)key.x4; + f1 = ((h1 >> 6) | (h2 << 20)) + (UInt64)key.x5; + f2 = ((h2 >> 12) | (h3 << 14)) + (UInt64)key.x6; + f3 = ((h3 >> 18) | (h4 << 8)) + (UInt64)key.x7; + + unchecked + { + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 0, (uint)f0); f1 += (f0 >> 32); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 4, (uint)f1); f2 += (f1 >> 32); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 8, (uint)f2); f3 += (f2 >> 32); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 12, (uint)f3); + } + } + } +} diff --git a/Internal/Salsa/Salsa20.cs b/Internal/Salsa/Salsa20.cs new file mode 100644 index 00000000..4578f643 --- /dev/null +++ b/Internal/Salsa/Salsa20.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace Chaos.NaCl.Internal.Salsa +{ + internal class Salsa20 + { + public const uint SalsaConst0 = 0x61707865; + public const uint SalsaConst1 = 0x3320646e; + public const uint SalsaConst2 = 0x79622d32; + public const uint SalsaConst3 = 0x6b206574; + + public static void HSalsa20(byte[] output, int outputOffset, byte[] key, int keyOffset, byte[] nonce, int nonceOffset) + { + Array16 state; + state.x0 = SalsaConst0; + state.x1 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 0); + state.x2 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 4); + state.x3 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 8); + state.x4 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 12); + state.x5 = SalsaConst1; + state.x6 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 0); + state.x7 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 4); + state.x8 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 8); + state.x9 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 12); + state.x10 = SalsaConst2; + state.x11 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 16); + state.x12 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 20); + state.x13 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 24); + state.x14 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 28); + state.x15 = SalsaConst3; + + SalsaCore.HSalsa(out state, ref state, 20); + + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 0, state.x0); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 4, state.x5); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 8, state.x10); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 12, state.x15); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 16, state.x6); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 20, state.x7); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 24, state.x8); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 28, state.x9); + } + } +} diff --git a/Internal/Salsa/SalsaCore.cs b/Internal/Salsa/SalsaCore.cs new file mode 100644 index 00000000..8e3ecad7 --- /dev/null +++ b/Internal/Salsa/SalsaCore.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; + +namespace Chaos.NaCl.Internal.Salsa +{ + internal static class SalsaCore + { + public static void HSalsa(out Array16 output, ref Array16 input, int rounds) + { + InternalAssert.Assert(rounds % 2 == 0, "Number of salsa rounds must be even"); + + int doubleRounds = rounds / 2; + + UInt32 x0 = input.x0; + UInt32 x1 = input.x1; + UInt32 x2 = input.x2; + UInt32 x3 = input.x3; + UInt32 x4 = input.x4; + UInt32 x5 = input.x5; + UInt32 x6 = input.x6; + UInt32 x7 = input.x7; + UInt32 x8 = input.x8; + UInt32 x9 = input.x9; + UInt32 x10 = input.x10; + UInt32 x11 = input.x11; + UInt32 x12 = input.x12; + UInt32 x13 = input.x13; + UInt32 x14 = input.x14; + UInt32 x15 = input.x15; + + unchecked + { + for (int i = 0; i < doubleRounds; i++) + { + UInt32 y; + + // row 0 + y = x0 + x12; + x4 ^= (y << 7) | (y >> (32 - 7)); + y = x4 + x0; + x8 ^= (y << 9) | (y >> (32 - 9)); + y = x8 + x4; + x12 ^= (y << 13) | (y >> (32 - 13)); + y = x12 + x8; + x0 ^= (y << 18) | (y >> (32 - 18)); + + // row 1 + y = x5 + x1; + x9 ^= (y << 7) | (y >> (32 - 7)); + y = x9 + x5; + x13 ^= (y << 9) | (y >> (32 - 9)); + y = x13 + x9; + x1 ^= (y << 13) | (y >> (32 - 13)); + y = x1 + x13; + x5 ^= (y << 18) | (y >> (32 - 18)); + + // row 2 + y = x10 + x6; + x14 ^= (y << 7) | (y >> (32 - 7)); + y = x14 + x10; + x2 ^= (y << 9) | (y >> (32 - 9)); + y = x2 + x14; + x6 ^= (y << 13) | (y >> (32 - 13)); + y = x6 + x2; + x10 ^= (y << 18) | (y >> (32 - 18)); + + // row 3 + y = x15 + x11; + x3 ^= (y << 7) | (y >> (32 - 7)); + y = x3 + x15; + x7 ^= (y << 9) | (y >> (32 - 9)); + y = x7 + x3; + x11 ^= (y << 13) | (y >> (32 - 13)); + y = x11 + x7; + x15 ^= (y << 18) | (y >> (32 - 18)); + + // column 0 + y = x0 + x3; + x1 ^= (y << 7) | (y >> (32 - 7)); + y = x1 + x0; + x2 ^= (y << 9) | (y >> (32 - 9)); + y = x2 + x1; + x3 ^= (y << 13) | (y >> (32 - 13)); + y = x3 + x2; + x0 ^= (y << 18) | (y >> (32 - 18)); + + // column 1 + y = x5 + x4; + x6 ^= (y << 7) | (y >> (32 - 7)); + y = x6 + x5; + x7 ^= (y << 9) | (y >> (32 - 9)); + y = x7 + x6; + x4 ^= (y << 13) | (y >> (32 - 13)); + y = x4 + x7; + x5 ^= (y << 18) | (y >> (32 - 18)); + + // column 2 + y = x10 + x9; + x11 ^= (y << 7) | (y >> (32 - 7)); + y = x11 + x10; + x8 ^= (y << 9) | (y >> (32 - 9)); + y = x8 + x11; + x9 ^= (y << 13) | (y >> (32 - 13)); + y = x9 + x8; + x10 ^= (y << 18) | (y >> (32 - 18)); + + // column 3 + y = x15 + x14; + x12 ^= (y << 7) | (y >> (32 - 7)); + y = x12 + x15; + x13 ^= (y << 9) | (y >> (32 - 9)); + y = x13 + x12; + x14 ^= (y << 13) | (y >> (32 - 13)); + y = x14 + x13; + x15 ^= (y << 18) | (y >> (32 - 18)); + } + } + + output.x0 = x0; + output.x1 = x1; + output.x2 = x2; + output.x3 = x3; + output.x4 = x4; + output.x5 = x5; + output.x6 = x6; + output.x7 = x7; + output.x8 = x8; + output.x9 = x9; + output.x10 = x10; + output.x11 = x11; + output.x12 = x12; + output.x13 = x13; + output.x14 = x14; + output.x15 = x15; + } + + public static void Salsa(out Array16 output, ref Array16 input, int rounds) + { + Array16 temp; + HSalsa(out temp, ref input, rounds); + unchecked + { + output.x0 = temp.x0 + input.x0; + output.x1 = temp.x1 + input.x1; + output.x2 = temp.x2 + input.x2; + output.x3 = temp.x3 + input.x3; + output.x4 = temp.x4 + input.x4; + output.x5 = temp.x5 + input.x5; + output.x6 = temp.x6 + input.x6; + output.x7 = temp.x7 + input.x7; + output.x8 = temp.x8 + input.x8; + output.x9 = temp.x9 + input.x9; + output.x10 = temp.x10 + input.x10; + output.x11 = temp.x11 + input.x11; + output.x12 = temp.x12 + input.x12; + output.x13 = temp.x13 + input.x13; + output.x14 = temp.x14 + input.x14; + output.x15 = temp.x15 + input.x15; + } + } + + /*public static void SalsaCore(int[] output, int outputOffset, int[] input, int inputOffset, int rounds) + { + if (rounds % 2 != 0) + throw new ArgumentException("rounds must be even"); + } + + +static void store_littleendian(unsigned char *x,uint32 u) +{ + x[0] = u; u >>= 8; + x[1] = u; u >>= 8; + x[2] = u; u >>= 8; + x[3] = u; +} + + public static void HSalsaCore(int[] output, int outputOffset, int[] input, int inputOffset, int rounds) + { + if (rounds % 2 != 0) + throw new ArgumentException("rounds must be even"); + static uint32 rotate(uint32 u,int c) +{ + return (u << c) | (u >> (32 - c)); +} + + + +int crypto_core( + unsigned char *out, + const unsigned char *in, + const unsigned char *k, + const unsigned char *c +) +{ + uint32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int i; + + x0 = load_littleendian(c + 0); + x1 = load_littleendian(k + 0); + x2 = load_littleendian(k + 4); + x3 = load_littleendian(k + 8); + x4 = load_littleendian(k + 12); + x5 = load_littleendian(c + 4); + x6 = load_littleendian(in + 0); + x7 = load_littleendian(in + 4); + x8 = load_littleendian(in + 8); + x9 = load_littleendian(in + 12); + x10 = load_littleendian(c + 8); + x11 = load_littleendian(k + 16); + x12 = load_littleendian(k + 20); + x13 = load_littleendian(k + 24); + x14 = load_littleendian(k + 28); + x15 = load_littleendian(c + 12); + + for (i = ROUNDS;i > 0;i -= 2) { + x4 ^= rotate( x0+x12, 7); + x8 ^= rotate( x4+ x0, 9); + x12 ^= rotate( x8+ x4,13); + x0 ^= rotate(x12+ x8,18); + x9 ^= rotate( x5+ x1, 7); + x13 ^= rotate( x9+ x5, 9); + x1 ^= rotate(x13+ x9,13); + x5 ^= rotate( x1+x13,18); + x14 ^= rotate(x10+ x6, 7); + x2 ^= rotate(x14+x10, 9); + x6 ^= rotate( x2+x14,13); + x10 ^= rotate( x6+ x2,18); + x3 ^= rotate(x15+x11, 7); + x7 ^= rotate( x3+x15, 9); + x11 ^= rotate( x7+ x3,13); + x15 ^= rotate(x11+ x7,18); + x1 ^= rotate( x0+ x3, 7); + x2 ^= rotate( x1+ x0, 9); + x3 ^= rotate( x2+ x1,13); + x0 ^= rotate( x3+ x2,18); + x6 ^= rotate( x5+ x4, 7); + x7 ^= rotate( x6+ x5, 9); + x4 ^= rotate( x7+ x6,13); + x5 ^= rotate( x4+ x7,18); + x11 ^= rotate(x10+ x9, 7); + x8 ^= rotate(x11+x10, 9); + x9 ^= rotate( x8+x11,13); + x10 ^= rotate( x9+ x8,18); + x12 ^= rotate(x15+x14, 7); + x13 ^= rotate(x12+x15, 9); + x14 ^= rotate(x13+x12,13); + x15 ^= rotate(x14+x13,18); + } + + store_littleendian(out + 0,x0); + store_littleendian(out + 4,x5); + store_littleendian(out + 8,x10); + store_littleendian(out + 12,x15); + store_littleendian(out + 16,x6); + store_littleendian(out + 20,x7); + store_littleendian(out + 24,x8); + store_littleendian(out + 28,x9); + + return 0; +}*/ + + } +} diff --git a/Internal/Salsa/replace regex.txt b/Internal/Salsa/replace regex.txt new file mode 100644 index 00000000..f4856899 --- /dev/null +++ b/Internal/Salsa/replace regex.txt @@ -0,0 +1,2 @@ +x(\d+) ^= rotate\((.+), (\d+)\); +y = \2;\r\nx\1 ^= (y << \3) | (y >> (32 - \3)); \ No newline at end of file diff --git a/Internal/Sha512Internal.cs b/Internal/Sha512Internal.cs new file mode 100644 index 00000000..37670d09 --- /dev/null +++ b/Internal/Sha512Internal.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Generic; + +namespace Chaos.NaCl.Internal +{ + internal static class Sha512Internal + { + private static readonly UInt64[] K = new UInt64[] + { + 0x428a2f98d728ae22,0x7137449123ef65cd,0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc, + 0x3956c25bf348b538,0x59f111f1b605d019,0x923f82a4af194f9b,0xab1c5ed5da6d8118, + 0xd807aa98a3030242,0x12835b0145706fbe,0x243185be4ee4b28c,0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f,0x80deb1fe3b1696b1,0x9bdc06a725c71235,0xc19bf174cf692694, + 0xe49b69c19ef14ad2,0xefbe4786384f25e3,0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65, + 0x2de92c6f592b0275,0x4a7484aa6ea6e483,0x5cb0a9dcbd41fbd4,0x76f988da831153b5, + 0x983e5152ee66dfab,0xa831c66d2db43210,0xb00327c898fb213f,0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2,0xd5a79147930aa725,0x06ca6351e003826f,0x142929670a0e6e70, + 0x27b70a8546d22ffc,0x2e1b21385c26c926,0x4d2c6dfc5ac42aed,0x53380d139d95b3df, + 0x650a73548baf63de,0x766a0abb3c77b2a8,0x81c2c92e47edaee6,0x92722c851482353b, + 0xa2bfe8a14cf10364,0xa81a664bbc423001,0xc24b8b70d0f89791,0xc76c51a30654be30, + 0xd192e819d6ef5218,0xd69906245565a910,0xf40e35855771202a,0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8,0x1e376c085141ab53,0x2748774cdf8eeb99,0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb,0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc,0x78a5636f43172f60,0x84c87814a1f0ab72,0x8cc702081a6439ec, + 0x90befffa23631e28,0xa4506cebde82bde9,0xbef9a3f7b2c67915,0xc67178f2e372532b, + 0xca273eceea26619c,0xd186b8c721c0c207,0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178, + 0x06f067aa72176fba,0x0a637dc5a2c898a6,0x113f9804bef90dae,0x1b710b35131c471b, + 0x28db77f523047d84,0x32caab7b40c72493,0x3c9ebe0a15c9bebc,0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6,0x597f299cfc657e2a,0x5fcb6fab3ad6faec,0x6c44198c4a475817 + }; + + internal static void Sha512Init(out Array8 state) + { + state.x0 = 0x6a09e667f3bcc908; + state.x1 = 0xbb67ae8584caa73b; + state.x2 = 0x3c6ef372fe94f82b; + state.x3 = 0xa54ff53a5f1d36f1; + state.x4 = 0x510e527fade682d1; + state.x5 = 0x9b05688c2b3e6c1f; + state.x6 = 0x1f83d9abfb41bd6b; + state.x7 = 0x5be0cd19137e2179; + } + + internal static void Core(out Array8 outputState, ref Array8 inputState, ref Array16 input) + { + unchecked + { + UInt64 a = inputState.x0; + UInt64 b = inputState.x1; + UInt64 c = inputState.x2; + UInt64 d = inputState.x3; + UInt64 e = inputState.x4; + UInt64 f = inputState.x5; + UInt64 g = inputState.x6; + UInt64 h = inputState.x7; + + UInt64 w0 = input.x0; + UInt64 w1 = input.x1; + UInt64 w2 = input.x2; + UInt64 w3 = input.x3; + UInt64 w4 = input.x4; + UInt64 w5 = input.x5; + UInt64 w6 = input.x6; + UInt64 w7 = input.x7; + UInt64 w8 = input.x8; + UInt64 w9 = input.x9; + UInt64 w10 = input.x10; + UInt64 w11 = input.x11; + UInt64 w12 = input.x12; + UInt64 w13 = input.x13; + UInt64 w14 = input.x14; + UInt64 w15 = input.x15; + + int t = 0; + while (true) + { + ulong t1, t2; + + {//0 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w0; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//1 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w1; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//2 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w2; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//3 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w3; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//4 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w4; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//5 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w5; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//6 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w6; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//7 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w7; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//8 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w8; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//9 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w9; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//10 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w10; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//11 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w11; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//12 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w12; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//13 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w13; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//14 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w14; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//15 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w15; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + if (t == 80) + break; + + w0 += ((w14 >> 19) ^ (w14 << (64 - 19)) ^ (w14 >> 61) ^ (w14 << (64 - 61)) ^ (w14 >> 6)) + + w9 + + ((w1 >> 1) ^ (w1 << (64 - 1)) ^ (w1 >> 8) ^ (w1 << (64 - 8)) ^ (w1 >> 7)); + w1 += ((w15 >> 19) ^ (w15 << (64 - 19)) ^ (w15 >> 61) ^ (w15 << (64 - 61)) ^ (w15 >> 6)) + + w10 + + ((w2 >> 1) ^ (w2 << (64 - 1)) ^ (w2 >> 8) ^ (w2 << (64 - 8)) ^ (w2 >> 7)); + w2 += ((w0 >> 19) ^ (w0 << (64 - 19)) ^ (w0 >> 61) ^ (w0 << (64 - 61)) ^ (w0 >> 6)) + + w11 + + ((w3 >> 1) ^ (w3 << (64 - 1)) ^ (w3 >> 8) ^ (w3 << (64 - 8)) ^ (w3 >> 7)); + w3 += ((w1 >> 19) ^ (w1 << (64 - 19)) ^ (w1 >> 61) ^ (w1 << (64 - 61)) ^ (w1 >> 6)) + + w12 + + ((w4 >> 1) ^ (w4 << (64 - 1)) ^ (w4 >> 8) ^ (w4 << (64 - 8)) ^ (w4 >> 7)); + w4 += ((w2 >> 19) ^ (w2 << (64 - 19)) ^ (w2 >> 61) ^ (w2 << (64 - 61)) ^ (w2 >> 6)) + + w13 + + ((w5 >> 1) ^ (w5 << (64 - 1)) ^ (w5 >> 8) ^ (w5 << (64 - 8)) ^ (w5 >> 7)); + w5 += ((w3 >> 19) ^ (w3 << (64 - 19)) ^ (w3 >> 61) ^ (w3 << (64 - 61)) ^ (w3 >> 6)) + + w14 + + ((w6 >> 1) ^ (w6 << (64 - 1)) ^ (w6 >> 8) ^ (w6 << (64 - 8)) ^ (w6 >> 7)); + w6 += ((w4 >> 19) ^ (w4 << (64 - 19)) ^ (w4 >> 61) ^ (w4 << (64 - 61)) ^ (w4 >> 6)) + + w15 + + ((w7 >> 1) ^ (w7 << (64 - 1)) ^ (w7 >> 8) ^ (w7 << (64 - 8)) ^ (w7 >> 7)); + w7 += ((w5 >> 19) ^ (w5 << (64 - 19)) ^ (w5 >> 61) ^ (w5 << (64 - 61)) ^ (w5 >> 6)) + + w0 + + ((w8 >> 1) ^ (w8 << (64 - 1)) ^ (w8 >> 8) ^ (w8 << (64 - 8)) ^ (w8 >> 7)); + w8 += ((w6 >> 19) ^ (w6 << (64 - 19)) ^ (w6 >> 61) ^ (w6 << (64 - 61)) ^ (w6 >> 6)) + + w1 + + ((w9 >> 1) ^ (w9 << (64 - 1)) ^ (w9 >> 8) ^ (w9 << (64 - 8)) ^ (w9 >> 7)); + w9 += ((w7 >> 19) ^ (w7 << (64 - 19)) ^ (w7 >> 61) ^ (w7 << (64 - 61)) ^ (w7 >> 6)) + + w2 + + ((w10 >> 1) ^ (w10 << (64 - 1)) ^ (w10 >> 8) ^ (w10 << (64 - 8)) ^ (w10 >> 7)); + w10 += ((w8 >> 19) ^ (w8 << (64 - 19)) ^ (w8 >> 61) ^ (w8 << (64 - 61)) ^ (w8 >> 6)) + + w3 + + ((w11 >> 1) ^ (w11 << (64 - 1)) ^ (w11 >> 8) ^ (w11 << (64 - 8)) ^ (w11 >> 7)); + w11 += ((w9 >> 19) ^ (w9 << (64 - 19)) ^ (w9 >> 61) ^ (w9 << (64 - 61)) ^ (w9 >> 6)) + + w4 + + ((w12 >> 1) ^ (w12 << (64 - 1)) ^ (w12 >> 8) ^ (w12 << (64 - 8)) ^ (w12 >> 7)); + w12 += ((w10 >> 19) ^ (w10 << (64 - 19)) ^ (w10 >> 61) ^ (w10 << (64 - 61)) ^ (w10 >> 6)) + + w5 + + ((w13 >> 1) ^ (w13 << (64 - 1)) ^ (w13 >> 8) ^ (w13 << (64 - 8)) ^ (w13 >> 7)); + w13 += ((w11 >> 19) ^ (w11 << (64 - 19)) ^ (w11 >> 61) ^ (w11 << (64 - 61)) ^ (w11 >> 6)) + + w6 + + ((w14 >> 1) ^ (w14 << (64 - 1)) ^ (w14 >> 8) ^ (w14 << (64 - 8)) ^ (w14 >> 7)); + w14 += ((w12 >> 19) ^ (w12 << (64 - 19)) ^ (w12 >> 61) ^ (w12 << (64 - 61)) ^ (w12 >> 6)) + + w7 + + ((w15 >> 1) ^ (w15 << (64 - 1)) ^ (w15 >> 8) ^ (w15 << (64 - 8)) ^ (w15 >> 7)); + w15 += ((w13 >> 19) ^ (w13 << (64 - 19)) ^ (w13 >> 61) ^ (w13 << (64 - 61)) ^ (w13 >> 6)) + + w8 + + ((w0 >> 1) ^ (w0 << (64 - 1)) ^ (w0 >> 8) ^ (w0 << (64 - 8)) ^ (w0 >> 7)); + } + + outputState.x0 = inputState.x0 + a; + outputState.x1 = inputState.x1 + b; + outputState.x2 = inputState.x2 + c; + outputState.x3 = inputState.x3 + d; + outputState.x4 = inputState.x4 + e; + outputState.x5 = inputState.x5 + f; + outputState.x6 = inputState.x6 + g; + outputState.x7 = inputState.x7 + h; + } + } + } +} diff --git a/MontgomeryCurve25519.cs b/MontgomeryCurve25519.cs new file mode 100644 index 00000000..562521f0 --- /dev/null +++ b/MontgomeryCurve25519.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using Chaos.NaCl.Internal; +using Chaos.NaCl.Internal.Ed25519Ref10; +using Chaos.NaCl.Internal.Salsa; + +namespace Chaos.NaCl +{ + // This class is mainly for compatibility with NaCl's Curve25519 implementation + // If you don't need that compatibility, use Ed25519.KeyExchange + public static class MontgomeryCurve25519 + { + public static readonly int PublicKeySizeInBytes = 32; + public static readonly int PrivateKeySizeInBytes = 32; + public static readonly int SharedKeySizeInBytes = 32; + + public static byte[] GetPublicKey(byte[] privateKey) + { + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (privateKey.Length != PrivateKeySizeInBytes) + throw new ArgumentException("privateKey.Length must be 32"); + var publicKey = new byte[32]; + GetPublicKey(new ArraySegment(publicKey), new ArraySegment(privateKey)); + return publicKey; + } + + static readonly byte[] _basePoint = new byte[32] + { + 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 ,0, 0, 0, 0, 0, + 0, 0, 0 ,0, 0, 0, 0, 0, + 0, 0, 0 ,0, 0, 0, 0, 0 + }; + + public static void GetPublicKey(ArraySegment publicKey, ArraySegment privateKey) + { + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey.Array"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException("privateKey.Count must be 32"); + if (privateKey.Count != PrivateKeySizeInBytes) + throw new ArgumentException("privateKey.Count must be 32"); + + // hack: abusing publicKey as temporary storage + // todo: remove hack + for (int i = 0; i < 32; i++) + { + publicKey.Array[publicKey.Offset + i] = privateKey.Array[privateKey.Offset + i]; + } + ScalarOperations.sc_clamp(publicKey.Array, publicKey.Offset); + + GroupElementP3 A; + GroupOperations.ge_scalarmult_base(out A, publicKey.Array, publicKey.Offset); + FieldElement publicKeyFE; + EdwardsToMontgomeryX(out publicKeyFE, ref A.Y, ref A.Z); + FieldOperations.fe_tobytes(publicKey.Array, publicKey.Offset, ref publicKeyFE); + } + + // hashes like the Curve25519 paper says + internal static void KeyExchangeOutputHashCurve25519Paper(byte[] sharedKey, int offset) + { + //c = Curve25519output + const UInt32 c0 = 'C' | 'u' << 8 | 'r' << 16 | (UInt32)'v' << 24; + const UInt32 c1 = 'e' | '2' << 8 | '5' << 16 | (UInt32)'5' << 24; + const UInt32 c2 = '1' | '9' << 8 | 'o' << 16 | (UInt32)'u' << 24; + const UInt32 c3 = 't' | 'p' << 8 | 'u' << 16 | (UInt32)'t' << 24; + + Array16 salsaState; + salsaState.x0 = c0; + salsaState.x1 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 0); + salsaState.x2 = 0; + salsaState.x3 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 4); + salsaState.x4 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 8); + salsaState.x5 = c1; + salsaState.x6 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 12); + salsaState.x7 = 0; + salsaState.x8 = 0; + salsaState.x9 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 16); + salsaState.x10 = c2; + salsaState.x11 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 20); + salsaState.x12 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 24); + salsaState.x13 = 0; + salsaState.x14 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 28); + salsaState.x15 = c3; + SalsaCore.Salsa(out salsaState, ref salsaState, 20); + + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 0, salsaState.x0); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 4, salsaState.x1); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 8, salsaState.x2); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 12, salsaState.x3); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 16, salsaState.x4); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 20, salsaState.x5); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 24, salsaState.x6); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 28, salsaState.x7); + } + + private static readonly byte[] _zero16 = new byte[16]; + + // hashes like the NaCl paper says instead i.e. HSalsa(x,0) + internal static void KeyExchangeOutputHashNaCl(byte[] sharedKey, int offset) + { + Salsa20.HSalsa20(sharedKey, offset, sharedKey, offset, _zero16, 0); + } + + public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey) + { + var sharedKey = new byte[SharedKeySizeInBytes]; + KeyExchange(new ArraySegment(sharedKey), new ArraySegment(publicKey), new ArraySegment(privateKey)); + return sharedKey; + } + + public static void KeyExchange(ArraySegment sharedKey, ArraySegment publicKey, ArraySegment privateKey) + { + if (sharedKey.Array == null) + throw new ArgumentNullException("sharedKey.Array"); + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey"); + if (sharedKey.Count != 32) + throw new ArgumentException("sharedKey.Count != 32"); + if (publicKey.Count != 32) + throw new ArgumentException("publicKey.Count != 32"); + if (privateKey.Count != 32) + throw new ArgumentException("privateKey.Count != 32"); + MontgomeryOperations.scalarmult(sharedKey.Array, sharedKey.Offset, privateKey.Array, privateKey.Offset, publicKey.Array, publicKey.Offset); + KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset); + } + + internal static void EdwardsToMontgomeryX(out FieldElement montgomeryX, ref FieldElement edwardsY, ref FieldElement edwardsZ) + { + FieldElement tempX, tempZ; + FieldOperations.fe_add(out tempX, ref edwardsZ, ref edwardsY); + FieldOperations.fe_sub(out tempZ, ref edwardsZ, ref edwardsY); + FieldOperations.fe_invert(out tempZ, ref tempZ); + FieldOperations.fe_mul(out montgomeryX, ref tempX, ref tempZ); + } + } +} diff --git a/OneTimeAuth.cs b/OneTimeAuth.cs new file mode 100644 index 00000000..805e6919 --- /dev/null +++ b/OneTimeAuth.cs @@ -0,0 +1,19 @@ +using System; + +namespace Chaos.NaCl +{ + public abstract class OneTimeAuth + { + private static readonly Poly1305 _poly1305 = new Poly1305(); + + public abstract int KeySizeInBytes { get; } + public abstract int SignatureSizeInBytes { get; } + + public abstract byte[] Sign(byte[] message, byte[] key); + public abstract void Sign(ArraySegment signature, ArraySegment message, ArraySegment key); + public abstract bool Verify(byte[] signature, byte[] message, byte[] key); + public abstract bool Verify(ArraySegment signature, ArraySegment message, ArraySegment key); + + public static OneTimeAuth Poly1305 { get { return _poly1305; } } + } +} diff --git a/Poly1305.cs b/Poly1305.cs new file mode 100644 index 00000000..4febd08d --- /dev/null +++ b/Poly1305.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using Chaos.NaCl.Internal; + +namespace Chaos.NaCl +{ + internal sealed class Poly1305 : OneTimeAuth + { + public override int KeySizeInBytes + { + get { return 32; } + } + + public override int SignatureSizeInBytes + { + get { return 16; } + } + + [Obsolete("Needs more testing")] + public override byte[] Sign(byte[] message, byte[] key) + { + if (message == null) + throw new ArgumentNullException("message"); + if (key == null) + throw new ArgumentNullException("key"); + if (key.Length != 32) + throw new ArgumentException("Invalid key size", "key"); + + var result = new byte[16]; + Array8 internalKey; + ByteIntegerConverter.Array8LoadLittleEndian32(out internalKey, key, 0); + Poly1305Donna.poly1305_auth(result, 0, message, 0, message.Length, ref internalKey); + return result; + } + + [Obsolete("Needs more testing")] + public override void Sign(ArraySegment signature, ArraySegment message, ArraySegment key) + { + if (signature.Array == null) + throw new ArgumentNullException("signature.Array"); + if (message.Array == null) + throw new ArgumentNullException("message.Array"); + if (key.Array == null) + throw new ArgumentNullException("key.Array"); + if (key.Count != 32) + throw new ArgumentException("Invalid key size", "key"); + if (signature.Count != 16) + throw new ArgumentException("Invalid signature size", "signature"); + + Array8 internalKey; + ByteIntegerConverter.Array8LoadLittleEndian32(out internalKey, key.Array, key.Offset); + Poly1305Donna.poly1305_auth(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, ref internalKey); + } + + [Obsolete("Needs more testing")] + public override bool Verify(byte[] signature, byte[] message, byte[] key) + { + if (signature == null) + throw new ArgumentNullException("signature"); + if (message == null) + throw new ArgumentNullException("message"); + if (key == null) + throw new ArgumentNullException("key"); + if (signature.Length != 16) + throw new ArgumentException("Invalid signature size", "signature"); + if (key.Length != 32) + throw new ArgumentException("Invalid key size", "key"); + + var tempBytes = new byte[16];//todo: remove allocation + Array8 internalKey; + ByteIntegerConverter.Array8LoadLittleEndian32(out internalKey, key, 0); + Poly1305Donna.poly1305_auth(tempBytes, 0, message, 0, message.Length, ref internalKey); + return CryptoBytes.ConstantTimeEquals(tempBytes, signature); + } + + [Obsolete("Needs more testing")] + public override bool Verify(ArraySegment signature, ArraySegment message, ArraySegment key) + { + if (signature.Array == null) + throw new ArgumentNullException("signature.Array"); + if (message.Array == null) + throw new ArgumentNullException("message.Array"); + if (key.Array == null) + throw new ArgumentNullException("key.Array"); + if (key.Count != 32) + throw new ArgumentException("Invalid key size", "key"); + if (signature.Count != 16) + throw new ArgumentException("Invalid signature size", "signature"); + + var tempBytes = new byte[16];//todo: remove allocation + Array8 internalKey; + ByteIntegerConverter.Array8LoadLittleEndian32(out internalKey, key.Array, key.Offset); + Poly1305Donna.poly1305_auth(tempBytes, 0, message.Array, message.Offset, message.Count, ref internalKey); + return CryptoBytes.ConstantTimeEquals(new ArraySegment(tempBytes), signature); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a8650f94 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Chaos.NaCl")] +[assembly: AssemblyDescription("C# port of the NaCl cryptography library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CodesInChaos")] +[assembly: AssemblyProduct("Chaos.NaCl cryptography library")] +[assembly: AssemblyCopyright("public domain")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("0.1.0.0")] + +[assembly: InternalsVisibleTo("Chaos.NaCl.Tests")] +[assembly: InternalsVisibleTo("Chaos.NaCl.Benchmark")] \ No newline at end of file diff --git a/Properties/AssemblyInfoFull.cs b/Properties/AssemblyInfoFull.cs new file mode 100644 index 00000000..80fd16c1 --- /dev/null +++ b/Properties/AssemblyInfoFull.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f07e7dd1-d31c-4994-8948-e42de7ef16ec")] \ No newline at end of file diff --git a/Sha512.cs b/Sha512.cs new file mode 100644 index 00000000..60d70466 --- /dev/null +++ b/Sha512.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using Chaos.NaCl.Internal; + +namespace Chaos.NaCl +{ + public class Sha512 + { + private Array8 _state; + private readonly byte[] _buffer; + private ulong _totalBytes; + public const int BlockSize = 128; + private static readonly byte[] _padding = new byte[] { 0x80 }; + + public Sha512() + { + _buffer = new byte[BlockSize];//todo: remove allocation + Init(); + } + + public void Init() + { + Sha512Internal.Sha512Init(out _state); + _totalBytes = 0; + } + + public void Update(ArraySegment data) + { + if (data.Array == null) + throw new ArgumentNullException("data.Array"); + Update(data.Array, data.Offset, data.Count); + } + + public void Update(byte[] data, int offset, int count) + { + if (data == null) + throw new ArgumentNullException("data"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + if (data.Length - offset < count) + throw new ArgumentException("Requires offset + count <= data.Length"); + + Array16 block; + int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); + _totalBytes += (uint)count; + + if (_totalBytes >= ulong.MaxValue / 8) + throw new InvalidOperationException("Too much data"); + // Fill existing buffer + if (bytesInBuffer != 0) + { + var toCopy = Math.Min(BlockSize - bytesInBuffer, count); + Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, toCopy); + offset += toCopy; + count -= 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 (count >= BlockSize) + { + ByteIntegerConverter.Array16LoadBigEndian64(out block, data, offset); + Sha512Internal.Core(out _state, ref _state, ref block); + offset += BlockSize; + count -= BlockSize; + } + // Copy remainder into buffer + if (count > 0) + { + Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, count); + } + } + + public void Finish(ArraySegment output) + { + if (output.Array == null) + throw new ArgumentNullException("output.Array"); + if (output.Count != 64) + throw new ArgumentException("output.Count must be 64"); + + Update(_padding, 0, _padding.Length); + Array16 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); + } + 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); + } + + public byte[] Finish() + { + var result = new byte[64]; + Finish(new ArraySegment(result)); + return result; + } + + public static byte[] Hash(byte[] data) + { + return Hash(data, 0, data.Length); + } + + public static byte[] Hash(byte[] data, int offset, int count) + { + var hasher = new Sha512(); + hasher.Update(data, offset, count); + return hasher.Finish(); + } + } +} diff --git a/XSalsa20Poly1305.cs b/XSalsa20Poly1305.cs new file mode 100644 index 00000000..4523e442 --- /dev/null +++ b/XSalsa20Poly1305.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using Chaos.NaCl.Internal; +using Chaos.NaCl.Internal.Salsa; + +namespace Chaos.NaCl +{ + public static class XSalsa20Poly1305 + { + public static readonly int KeySizeInBytes = 32; + public static readonly int NonceSizeInBytes = 24; + public static readonly int MacSizeInBytes = 16; + + public static byte[] Encrypt(byte[] message, byte[] key, byte[] nonce) + { + if (message == null) + throw new ArgumentNullException("message"); + if (key == null) + throw new ArgumentNullException("key"); + if (nonce == null) + throw new ArgumentNullException("nonce"); + if (key.Length != KeySizeInBytes) + throw new ArgumentException("key.Length != 32"); + if (nonce.Length != NonceSizeInBytes) + throw new ArgumentException("nonce.Length != 24"); + + var ciphertext = new byte[message.Length + MacSizeInBytes]; + EncryptInternal(ciphertext, 0, message, 0, message.Length, key, 0, nonce, 0); + return ciphertext; + } + + public static void Encrypt(ArraySegment ciphertext, ArraySegment message, ArraySegment key, ArraySegment nonce) + { + if (key.Count != KeySizeInBytes) + throw new ArgumentException("key.Length != 32"); + if (nonce.Count != NonceSizeInBytes) + throw new ArgumentException("nonce.Length != 24"); + if (ciphertext.Count != message.Count + MacSizeInBytes) + throw new ArgumentException("ciphertext.Count != message.Count + 16"); + EncryptInternal(ciphertext.Array, ciphertext.Offset, message.Array, message.Offset, message.Count, key.Array, key.Offset, nonce.Array, nonce.Offset); + } + + /// + /// Decrypts the ciphertext and verifies its authenticity + /// + /// Plaintext if MAC validation succeeds, null if the data is invalid. + public static byte[] TryDecrypt(byte[] ciphertext, byte[] key, byte[] nonce) + { + if (ciphertext == null) + throw new ArgumentNullException("ciphertext"); + if (key == null) + throw new ArgumentNullException("key"); + if (nonce == null) + throw new ArgumentNullException("nonce"); + if (key.Length != KeySizeInBytes) + throw new ArgumentException("key.Length != 32"); + if (nonce.Length != NonceSizeInBytes) + throw new ArgumentException("nonce.Length != 24"); + + if (ciphertext.Length < MacSizeInBytes) + return null; + var plaintext = new byte[ciphertext.Length - MacSizeInBytes]; + bool success = DecryptInternal(plaintext, 0, ciphertext, 0, ciphertext.Length, key, 0, nonce, 0); + if (success) + return plaintext; + else + return null; + } + + /// + /// Decrypts the ciphertext and verifies its authenticity + /// + /// Plaintext if authentication succeeded, all zero if authentication failed, unmodified if argument verification fails + /// + /// Symmetric key. Must be identical to key specified for encryption. + /// Must be identical to nonce specified for encryption. + /// true if ciphertext is authentic, false otherwise + public static bool TryDecrypt(ArraySegment message, ArraySegment ciphertext, ArraySegment key, ArraySegment nonce) + { + if (key.Count != KeySizeInBytes) + throw new ArgumentException("key.Length != 32"); + if (nonce.Count != NonceSizeInBytes) + throw new ArgumentException("nonce.Length != 24"); + if (ciphertext.Count != message.Count + MacSizeInBytes) + throw new ArgumentException("ciphertext.Count != message.Count + 16"); + + return DecryptInternal(message.Array, message.Offset, ciphertext.Array, ciphertext.Offset, ciphertext.Count, key.Array, key.Offset, nonce.Array, nonce.Offset); + } + + private static void PrepareInternalKey(out Array16 internalKey, byte[] key, int keyOffset, byte[] nonce, int nonceOffset) + { + internalKey.x0 = Salsa20.SalsaConst0; + internalKey.x1 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 0); + internalKey.x2 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 4); + internalKey.x3 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 8); + internalKey.x4 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 12); + internalKey.x5 = Salsa20.SalsaConst1; + internalKey.x6 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 0); + internalKey.x7 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 4); + internalKey.x8 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 8); + internalKey.x9 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 12); + internalKey.x10 = Salsa20.SalsaConst2; + internalKey.x11 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 16); + internalKey.x12 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 20); + internalKey.x13 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 24); + internalKey.x14 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 28); + internalKey.x15 = Salsa20.SalsaConst3; + SalsaCore.HSalsa(out internalKey, ref internalKey, 20); + + //key + internalKey.x1 = internalKey.x0; + internalKey.x2 = internalKey.x5; + internalKey.x3 = internalKey.x10; + internalKey.x4 = internalKey.x15; + internalKey.x11 = internalKey.x6; + internalKey.x12 = internalKey.x7; + internalKey.x13 = internalKey.x8; + internalKey.x14 = internalKey.x9; + //const + internalKey.x0 = Salsa20.SalsaConst0; + internalKey.x5 = Salsa20.SalsaConst1; + internalKey.x10 = Salsa20.SalsaConst2; + internalKey.x15 = Salsa20.SalsaConst3; + //nonce + internalKey.x6 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 16); + internalKey.x7 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 20); + //offset + internalKey.x8 = 0; + internalKey.x9 = 0; + } + + private static bool DecryptInternal(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int ciphertextLength, byte[] key, int keyOffset, byte[] nonce, int nonceOffset) + { + int plaintextLength = ciphertextLength - MacSizeInBytes; + Array16 internalKey; + PrepareInternalKey(out internalKey, key, keyOffset, nonce, nonceOffset); + + Array16 temp; + var tempBytes = new byte[64];//todo: remove allocation + + // first iteration + { + SalsaCore.Salsa(out temp, ref internalKey, 20); + + //first half is for Poly1305 + Array8 poly1305Key; + poly1305Key.x0 = temp.x0; + poly1305Key.x1 = temp.x1; + poly1305Key.x2 = temp.x2; + poly1305Key.x3 = temp.x3; + poly1305Key.x4 = temp.x4; + poly1305Key.x5 = temp.x5; + poly1305Key.x6 = temp.x6; + poly1305Key.x7 = temp.x7; + + // compute MAC + Poly1305Donna.poly1305_auth(tempBytes, 0, ciphertext, ciphertextOffset + 16, plaintextLength, ref poly1305Key); + if (!CryptoBytes.ConstantTimeEquals(tempBytes, 0, ciphertext, ciphertextOffset, MacSizeInBytes)) + { + Array.Clear(plaintext, plaintextOffset, plaintextLength); + return false; + } + + // rest for the message + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 0, temp.x8); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 4, temp.x9); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 8, temp.x10); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 12, temp.x11); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 16, temp.x12); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 20, temp.x13); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 24, temp.x14); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 28, temp.x15); + int count = Math.Min(32, plaintextLength); + for (int i = 0; i < count; i++) + plaintext[plaintextOffset + i] = (byte)(ciphertext[MacSizeInBytes + ciphertextOffset + i] ^ tempBytes[i]); + } + + // later iterations + int blockOffset = 32; + while (blockOffset < plaintextLength) + { + internalKey.x8++; + SalsaCore.Salsa(out temp, ref internalKey, 20); + ByteIntegerConverter.Array16StoreLittleEndian32(tempBytes, 0, ref temp); + int count = Math.Min(64, plaintextLength - blockOffset); + for (int i = 0; i < count; i++) + plaintext[plaintextOffset + blockOffset + i] = (byte)(ciphertext[16 + ciphertextOffset + blockOffset + i] ^ tempBytes[i]); + blockOffset += 64; + } + return true; + } + + private static void EncryptInternal(byte[] ciphertext, int ciphertextOffset, byte[] message, int messageOffset, int messageLength, byte[] key, int keyOffset, byte[] nonce, int nonceOffset) + { + Array16 internalKey; + PrepareInternalKey(out internalKey, key, keyOffset, nonce, nonceOffset); + + Array16 temp; + var tempBytes = new byte[64];//todo: remove allocation + Array8 poly1305Key; + + // first iteration + { + SalsaCore.Salsa(out temp, ref internalKey, 20); + + //first half is for Poly1305 + poly1305Key.x0 = temp.x0; + poly1305Key.x1 = temp.x1; + poly1305Key.x2 = temp.x2; + poly1305Key.x3 = temp.x3; + poly1305Key.x4 = temp.x4; + poly1305Key.x5 = temp.x5; + poly1305Key.x6 = temp.x6; + poly1305Key.x7 = temp.x7; + + // second half for the message + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 0, temp.x8); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 4, temp.x9); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 8, temp.x10); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 12, temp.x11); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 16, temp.x12); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 20, temp.x13); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 24, temp.x14); + ByteIntegerConverter.StoreLittleEndian32(tempBytes, 28, temp.x15); + int count = Math.Min(32, messageLength); + for (int i = 0; i < count; i++) + ciphertext[16 + ciphertextOffset + i] = (byte)(message[messageOffset + i] ^ tempBytes[i]); + } + + // later iterations + int blockOffset = 32; + while (blockOffset < messageLength) + { + internalKey.x8++; + SalsaCore.Salsa(out temp, ref internalKey, 20); + ByteIntegerConverter.Array16StoreLittleEndian32(tempBytes, 0, ref temp); + int count = Math.Min(64, messageLength - blockOffset); + for (int i = 0; i < count; i++) + ciphertext[16 + ciphertextOffset + blockOffset + i] = (byte)(message[messageOffset + blockOffset + i] ^ tempBytes[i]); + blockOffset += 64; + } + + // compute MAC + Poly1305Donna.poly1305_auth(ciphertext, ciphertextOffset, ciphertext, ciphertextOffset + 16, messageLength, ref poly1305Key); + } + } +} From fd9413cc52cefe7a466a0137181f7625d7a43dc5 Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Fri, 19 Oct 2018 10:57:34 -0400 Subject: [PATCH 03/10] Implement EdDSA signature algorithm support via Chaos.NaCl --- fido2-net-lib.sln | 6 ++++++ fido2-net-lib/AuthDataHelper.cs | 2 +- fido2-net-lib/Fido2NetLib.csproj | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fido2-net-lib.sln b/fido2-net-lib.sln index 7c2448dd..f770744f 100644 --- a/fido2-net-lib.sln +++ b/fido2-net-lib.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaos.NaCl", "Chaos.NaCl\Chaos.NaCl.csproj", "{AE28FD14-7985-4707-A963-C94B8597AE50}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +34,10 @@ Global {06E82E3F-C626-4070-98BB-6D307DA86AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {06E82E3F-C626-4070-98BB-6D307DA86AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {06E82E3F-C626-4070-98BB-6D307DA86AC2}.Release|Any CPU.Build.0 = Release|Any CPU + {AE28FD14-7985-4707-A963-C94B8597AE50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE28FD14-7985-4707-A963-C94B8597AE50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE28FD14-7985-4707-A963-C94B8597AE50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE28FD14-7985-4707-A963-C94B8597AE50}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/fido2-net-lib/AuthDataHelper.cs b/fido2-net-lib/AuthDataHelper.cs index 4c5058ef..04ce1cb4 100644 --- a/fido2-net-lib/AuthDataHelper.cs +++ b/fido2-net-lib/AuthDataHelper.cs @@ -109,7 +109,7 @@ public static bool VerifySigWithCoseKey(byte[] data, PeterO.Cbor.CBORObject cose switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves { case 6: - throw new Fido2VerificationException("ALG_SIGN_ED25519_EDDSA_SHA512_RAW support not yet implmented"); + return Chaos.NaCl.Ed25519.Verify(sig, GetHasher(HashAlgorithmName.SHA512).ComputeHash(data), coseKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString()); default: throw new ArgumentOutOfRangeException("crv"); } diff --git a/fido2-net-lib/Fido2NetLib.csproj b/fido2-net-lib/Fido2NetLib.csproj index 9166dd25..fbfa056f 100644 --- a/fido2-net-lib/Fido2NetLib.csproj +++ b/fido2-net-lib/Fido2NetLib.csproj @@ -24,7 +24,11 @@ - + + + + + From 2ff0b5279e70703ccdf7af0a2f54621eab1b6d49 Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Fri, 19 Oct 2018 11:11:46 -0400 Subject: [PATCH 04/10] Change PeterO.Cbor reference from package to project --- CBOR/CBOR.csproj | 4 +- CBOR/PeterO/Cbor/CBORDataUtilities.cs | 8 +- CBOR/PeterO/Cbor/CBOREncodeOptions.cs | 28 +- CBOR/PeterO/Cbor/CBORException.cs | 8 +- CBOR/PeterO/Cbor/CBORObject.cs | 348 ++++----- CBOR/PeterO/Cbor/CBORObjectExtra.cs | 52 +- CBOR/PeterO/Cbor/CBORObjectMath.cs | 2 +- CBOR/PeterO/Cbor/CBORTag3.cs | 2 +- CBOR/PeterO/Cbor/CBORTag37.cs | 2 +- CBOR/PeterO/Cbor/CBORTagGenericString.cs | 2 +- CBOR/PeterO/Cbor/CBORType.cs | 16 +- CBOR/PeterO/Cbor/CBORTypeFilter.cs | 56 +- CBOR/PeterO/Cbor/CBORTypeMapper.cs | 10 +- CBOR/PeterO/Cbor/CBORUtilities.cs | 2 +- CBOR/PeterO/Cbor/CBORUuidConverter.cs | 2 +- CBOR/PeterO/Cbor/CharacterReader.cs | 24 +- CBOR/PeterO/Cbor/FastInteger2.cs | 8 +- CBOR/PeterO/Cbor/ICBORConverter.cs | 4 +- CBOR/PeterO/Cbor/ICBORTag.cs | 6 +- CBOR/PeterO/Cbor/ICBORToFromConverter.cs | 4 +- CBOR/PeterO/Cbor/ICharacterInput.cs | 6 +- CBOR/PeterO/Cbor/JSONOptions.cs | 10 +- CBOR/PeterO/Cbor/PODOptions.cs | 12 +- CBOR/PeterO/Cbor/StringRefs.cs | 2 +- CBOR/PeterO/Cbor/URIUtility.cs | 32 +- CBOR/docs.xml | 710 +++++++++--------- fido2-net-lib.sln | 6 + fido2-net-lib/AuthDataHelper.cs | 35 +- .../AuthenticatorAttestationResponse.cs | 79 +- fido2-net-lib/Fido2NetLib.csproj | 2 +- 30 files changed, 745 insertions(+), 737 deletions(-) diff --git a/CBOR/CBOR.csproj b/CBOR/CBOR.csproj index 63e2742e..3475503b 100644 --- a/CBOR/CBOR.csproj +++ b/CBOR/CBOR.csproj @@ -2,7 +2,7 @@ netstandard1.0 - True + false 3.4.0-beta1 Peter Occil A C# implementation of Concise Binary Object Representation (CBOR), a general-purpose binary data format defined in RFC 7049. @@ -32,7 +32,7 @@ Version 3.4.0-alpha1: cbor data serialization binary json - True + false PeterO.snk CBOR (Concise Binary Object Representation) true diff --git a/CBOR/PeterO/Cbor/CBORDataUtilities.cs b/CBOR/PeterO/Cbor/CBORDataUtilities.cs index bb86858d..25b38ee7 100644 --- a/CBOR/PeterO/Cbor/CBORDataUtilities.cs +++ b/CBOR/PeterO/Cbor/CBORDataUtilities.cs @@ -11,18 +11,18 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORDataUtilities"]/*'/> public static class CBORDataUtilities { private const int MaxSafeInt = 214748363; /// + /// path='docs/doc[@name="M:CBORDataUtilities.ParseJSONNumber(System.String)"]/*'/> public static CBORObject ParseJSONNumber(string str) { return ParseJSONNumber(str, false, false); } /// + /// path='docs/doc[@name="M:CBORDataUtilities.ParseJSONNumber(System.String,System.Boolean,System.Boolean)"]/*'/> public static CBORObject ParseJSONNumber( string str, bool integersOnly, @@ -31,7 +31,7 @@ public static class CBORDataUtilities { } /// + /// path='docs/doc[@name="M:CBORDataUtilities.ParseJSONNumber(System.String,System.Boolean,System.Boolean,System.Boolean)"]/*'/> public static CBORObject ParseJSONNumber( string str, bool integersOnly, diff --git a/CBOR/PeterO/Cbor/CBOREncodeOptions.cs b/CBOR/PeterO/Cbor/CBOREncodeOptions.cs index c8024b85..bfb03e69 100644 --- a/CBOR/PeterO/Cbor/CBOREncodeOptions.cs +++ b/CBOR/PeterO/Cbor/CBOREncodeOptions.cs @@ -2,27 +2,27 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBOREncodeOptions"]/*'/> public sealed class CBOREncodeOptions { /// + /// path='docs/doc[@name="F:CBOREncodeOptions.None"]/*'/> [Obsolete("Use 'new CBOREncodeOptions(true,true)' instead. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated. 'CBOREncodeOptions.Default' contains recommended default options that may be adopted by certain CBORObject methods in the next major version.")] public static readonly CBOREncodeOptions None = new CBOREncodeOptions(0); /// + /// path='docs/doc[@name="F:CBOREncodeOptions.Default"]/*'/> public static readonly CBOREncodeOptions Default = new CBOREncodeOptions(false, false); /// + /// path='docs/doc[@name="F:CBOREncodeOptions.NoIndefLengthStrings"]/*'/> [Obsolete("Use 'new CBOREncodeOptions(false,true)' instead. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] public static readonly CBOREncodeOptions NoIndefLengthStrings = new CBOREncodeOptions(1); /// + /// path='docs/doc[@name="F:CBOREncodeOptions.NoDuplicateKeys"]/*'/> [Obsolete("Use 'new CBOREncodeOptions(true,false)' instead. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] public static readonly CBOREncodeOptions NoDuplicateKeys = new CBOREncodeOptions(2); @@ -30,12 +30,12 @@ public sealed class CBOREncodeOptions { private readonly int value; /// + /// path='docs/doc[@name="M:CBOREncodeOptions.#ctor"]/*'/> public CBOREncodeOptions() : this(false, false) { } /// + /// path='docs/doc[@name="M:CBOREncodeOptions.#ctor(System.Boolean,System.Boolean)"]/*'/> public CBOREncodeOptions( bool useIndefLengthStrings, bool allowDuplicateKeys) : @@ -43,7 +43,7 @@ public sealed class CBOREncodeOptions { } /// + /// path='docs/doc[@name="M:CBOREncodeOptions.#ctor(System.Boolean,System.Boolean,System.Boolean)"]/*'/> public CBOREncodeOptions( bool useIndefLengthStrings, bool allowDuplicateKeys, @@ -60,7 +60,7 @@ public sealed class CBOREncodeOptions { } /// + /// path='docs/doc[@name="P:CBOREncodeOptions.UseIndefLengthStrings"]/*'/> public bool UseIndefLengthStrings { get { return (this.value & 1) == 0; @@ -68,7 +68,7 @@ public sealed class CBOREncodeOptions { } /// + /// path='docs/doc[@name="P:CBOREncodeOptions.AllowDuplicateKeys"]/*'/> public bool AllowDuplicateKeys { get { return (this.value & 2) == 0; @@ -76,11 +76,11 @@ public sealed class CBOREncodeOptions { } /// + /// path='docs/doc[@name="P:CBOREncodeOptions.Ctap2Canonical"]/*'/> public bool Ctap2Canonical { get; private set; } /// + /// path='docs/doc[@name="P:CBOREncodeOptions.Value"]/*'/> [Obsolete("Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] public int Value { get { @@ -93,14 +93,14 @@ public sealed class CBOREncodeOptions { } /// + /// path='docs/doc[@name="M:CBOREncodeOptions.Or(CBOREncodeOptions)"]/*'/> [Obsolete("May be removed in a later version. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] public CBOREncodeOptions Or(CBOREncodeOptions o) { return new CBOREncodeOptions(this.value | o.value); } /// + /// path='docs/doc[@name="M:CBOREncodeOptions.And(CBOREncodeOptions)"]/*'/> [Obsolete("May be removed in a later version. Option classes in this library will follow the form seen in JSONOptions in a later version; the approach used in this class is too complicated.")] public CBOREncodeOptions And(CBOREncodeOptions o) { return new CBOREncodeOptions(this.value & o.value); diff --git a/CBOR/PeterO/Cbor/CBORException.cs b/CBOR/PeterO/Cbor/CBORException.cs index c1632bb0..e49b05f5 100644 --- a/CBOR/PeterO/Cbor/CBORException.cs +++ b/CBOR/PeterO/Cbor/CBORException.cs @@ -8,20 +8,20 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORException"]/*'/> public class CBORException : Exception { /// + /// path='docs/doc[@name="M:CBORException.#ctor"]/*'/> public CBORException() { } /// + /// path='docs/doc[@name="M:CBORException.#ctor(System.String)"]/*'/> public CBORException(string message) : base(message) { } /// + /// path='docs/doc[@name="M:CBORException.#ctor(System.String,System.Exception)"]/*'/> public CBORException(string message, Exception innerException) : base(message, innerException) { } diff --git a/CBOR/PeterO/Cbor/CBORObject.cs b/CBOR/PeterO/Cbor/CBORObject.cs index 1f401452..142d9bbc 100644 --- a/CBOR/PeterO/Cbor/CBORObject.cs +++ b/CBOR/PeterO/Cbor/CBORObject.cs @@ -14,7 +14,7 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORObject"]/*'/> public sealed partial class CBORObject : IComparable, IEquatable { private static CBORObject ConstructSimpleValue(int v) { @@ -26,7 +26,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="F:CBORObject.False"]/*'/> #if CODE_ANALYSIS [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Security", @@ -38,16 +38,16 @@ public sealed partial class CBORObject : IComparable, CBORObject.ConstructSimpleValue(20); /// + /// path='docs/doc[@name="F:CBORObject.NaN"]/*'/> public static readonly CBORObject NaN = CBORObject.FromObject(Double.NaN); /// + /// path='docs/doc[@name="F:CBORObject.NegativeInfinity"]/*'/> public static readonly CBORObject NegativeInfinity = CBORObject.FromObject(Double.NegativeInfinity); /// + /// path='docs/doc[@name="F:CBORObject.Null"]/*'/> #if CODE_ANALYSIS [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Security", @@ -59,12 +59,12 @@ public sealed partial class CBORObject : IComparable, CBORObject.ConstructSimpleValue(22); /// + /// path='docs/doc[@name="F:CBORObject.PositiveInfinity"]/*'/> public static readonly CBORObject PositiveInfinity = CBORObject.FromObject(Double.PositiveInfinity); /// + /// path='docs/doc[@name="F:CBORObject.True"]/*'/> #if CODE_ANALYSIS [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Security", @@ -76,7 +76,7 @@ public sealed partial class CBORObject : IComparable, CBORObject.ConstructSimpleValue(21); /// + /// path='docs/doc[@name="F:CBORObject.Undefined"]/*'/> #if CODE_ANALYSIS [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Security", @@ -88,7 +88,7 @@ public sealed partial class CBORObject : IComparable, CBORObject.ConstructSimpleValue(23); /// + /// path='docs/doc[@name="F:CBORObject.Zero"]/*'/> public static readonly CBORObject Zero = CBORObject.ConstructIntegerValue(0); @@ -198,7 +198,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Count"]/*'/> public int Count { get { return (this.ItemType == CBORObjectTypeArray) ? this.AsList().Count : @@ -207,7 +207,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.InnermostTag"]/*'/> [Obsolete("Use MostInnerTag instead.")] public BigInteger InnermostTag { get { @@ -218,7 +218,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.OutermostTag"]/*'/> [Obsolete("Use MostOuterTag instead.")] public BigInteger OutermostTag { get { @@ -228,7 +228,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.MostInnerTag"]/*'/> public EInteger MostInnerTag { get { if (!this.IsTagged) { @@ -251,7 +251,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsFalse"]/*'/> public bool IsFalse { get { return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem @@ -260,7 +260,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsFinite"]/*'/> public bool IsFinite { get { return this.Type == CBORType.Number && !this.IsInfinity() && @@ -269,7 +269,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsIntegral"]/*'/> public bool IsIntegral { get { ICBORNumber cn = NumberInterfaces[this.ItemType]; @@ -278,7 +278,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsNull"]/*'/> public bool IsNull { get { return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem @@ -287,7 +287,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsTagged"]/*'/> public bool IsTagged { get { return this.itemtypeValue == CBORObjectTypeTagged; @@ -295,7 +295,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsTrue"]/*'/> public bool IsTrue { get { return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem @@ -304,7 +304,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsUndefined"]/*'/> public bool IsUndefined { get { return this.ItemType == CBORObjectTypeSimpleValue && (int)this.ThisItem @@ -313,7 +313,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsZero"]/*'/> public bool IsZero { get { ICBORNumber cn = NumberInterfaces[this.ItemType]; @@ -322,7 +322,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Keys"]/*'/> public ICollection Keys { get { if (this.ItemType == CBORObjectTypeMap) { @@ -334,7 +334,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.IsNegative"]/*'/> public bool IsNegative { get { ICBORNumber cn = NumberInterfaces[this.ItemType]; @@ -343,7 +343,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.MostOuterTag"]/*'/> public EInteger MostOuterTag { get { if (!this.IsTagged) { @@ -360,7 +360,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Sign"]/*'/> public int Sign { get { int ret = GetSignInternal(this.ItemType, this.ThisItem); @@ -372,7 +372,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.SimpleValue"]/*'/> public int SimpleValue { get { return (this.ItemType == CBORObjectTypeSimpleValue) ? @@ -381,7 +381,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Type"]/*'/> public CBORType Type { get { switch (this.ItemType) { @@ -411,7 +411,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Values"]/*'/> public ICollection Values { get { if (this.ItemType == CBORObjectTypeMap) { @@ -448,7 +448,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Item(System.Int32)"]/*'/> public CBORObject this[int index] { get { if (this.ItemType == CBORObjectTypeArray) { @@ -475,7 +475,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Item(CBORObject)"]/*'/> public CBORObject this[CBORObject key] { get { if (key == null) { @@ -505,7 +505,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="P:CBORObject.Item(System.String)"]/*'/> public CBORObject this[string key] { get { if (key == null) { @@ -533,7 +533,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AddConverter``1(System.Type,ICBORConverter{``0})"]/*'/> [Obsolete("To be replaced with the AddConverter method of CBORTypeMapper.")] public static void AddConverter(Type type, ICBORConverter converter) { if (type == null) { @@ -558,13 +558,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Addition(CBORObject,CBORObject)"]/*'/> public static CBORObject Addition(CBORObject first, CBORObject second) { return CBORObjectMath.Addition(first, second); } /// + /// path='docs/doc[@name="M:CBORObject.AddTagHandler(PeterO.BigInteger,ICBORTag)"]/*'/> [Obsolete("May be removed in the future without replacement. Not as useful as ICBORConverters and ICBORObjectConverters for FromObject and ToObject. Moreover, registering tag handlers as this method does may tie them to the lifetime of the application.")] public static void AddTagHandler(BigInteger bigintTag, ICBORTag handler) { if (bigintTag == null) { @@ -577,7 +577,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AddTagHandler(PeterO.Numbers.EInteger,ICBORTag)"]/*'/> [Obsolete("May be removed in the future without replacement. Not as useful as ICBORConverters and ICBORObjectConverters for FromObject and ToObject. Moreover, registering tag handlers as this method does may tie them to the lifetime of the application.")] public static void AddTagHandler(EInteger bigintTag, ICBORTag handler) { if (bigintTag == null) { @@ -601,13 +601,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.DecodeFromBytes(System.Byte[])"]/*'/> public static CBORObject DecodeFromBytes(byte[] data) { return DecodeFromBytes(data, new CBOREncodeOptions(true, true)); } /// + /// path='docs/doc[@name="M:CBORObject.DecodeFromBytes(System.Byte[],CBOREncodeOptions)"]/*'/> public static CBORObject DecodeFromBytes( byte[] data, CBOREncodeOptions options) { @@ -651,19 +651,19 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Divide(CBORObject,CBORObject)"]/*'/> public static CBORObject Divide(CBORObject first, CBORObject second) { return CBORObjectMath.Divide(first, second); } /// + /// path='docs/doc[@name="M:CBORObject.FromJSONString(System.String)"]/*'/> public static CBORObject FromJSONString(string str) { return FromJSONString(str, new CBOREncodeOptions(true, true)); } /// + /// path='docs/doc[@name="M:CBORObject.FromJSONString(System.String,CBOREncodeOptions)"]/*'/> public static CBORObject FromJSONString( string str, CBOREncodeOptions options) { @@ -692,13 +692,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ToObject(System.Type)"]/*'/> public object ToObject(Type t) { return this.ToObject(t, null, null, 0); } /// + /// path='docs/doc[@name="M:CBORObject.ToObject(System.Type,CBORTypeMapper)"]/*'/> public object ToObject(Type t, CBORTypeMapper mapper) { if (mapper == null) { throw new ArgumentNullException(nameof(mapper)); @@ -707,7 +707,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ToObject(System.Type,PODOptions)"]/*'/> public object ToObject(Type t, PODOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); @@ -716,7 +716,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ToObject(System.Type,CBORTypeMapper,PODOptions)"]/*'/> public object ToObject(Type t, CBORTypeMapper mapper, PODOptions options) { if (mapper == null) { throw new ArgumentNullException(nameof(mapper)); @@ -759,20 +759,20 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Int64)"]/*'/> public static CBORObject FromObject(long value) { return (value >= 0L && value < 24L) ? valueFixedObjects[(int)value] : (new CBORObject(CBORObjectTypeInteger, value)); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(CBORObject)"]/*'/> public static CBORObject FromObject(CBORObject value) { return value ?? CBORObject.Null; } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.BigInteger)"]/*'/> [Obsolete("Use the EInteger version of this method.")] public static CBORObject FromObject(BigInteger bigintValue) { return ((object)bigintValue == (object)null) ? CBORObject.Null : @@ -780,7 +780,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.Numbers.EInteger)"]/*'/> public static CBORObject FromObject(EInteger bigintValue) { if ((object)bigintValue == (object)null) { return CBORObject.Null; @@ -795,7 +795,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.ExtendedFloat)"]/*'/> [Obsolete("Use the EFloat version of this method instead.")] public static CBORObject FromObject(ExtendedFloat bigValue) { return ((object)bigValue == (object)null) ? CBORObject.Null : @@ -803,7 +803,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.Numbers.EFloat)"]/*'/> public static CBORObject FromObject(EFloat bigValue) { if ((object)bigValue == (object)null) { return CBORObject.Null; @@ -825,7 +825,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.ExtendedRational)"]/*'/> [Obsolete("Use the ERational version of this method instead.")] public static CBORObject FromObject(ExtendedRational bigValue) { return ((object)bigValue == (object)null) ? CBORObject.Null : @@ -833,7 +833,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.Numbers.ERational)"]/*'/> public static CBORObject FromObject(ERational bigValue) { if ((object)bigValue == (object)null) { return CBORObject.Null; @@ -853,7 +853,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.Numbers.EDecimal)"]/*'/> public static CBORObject FromObject(EDecimal otherValue) { if ((object)otherValue == (object)null) { return CBORObject.Null; @@ -875,7 +875,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(PeterO.ExtendedDecimal)"]/*'/> [Obsolete("Use the EDecimal version of this method instead.")] public static CBORObject FromObject(ExtendedDecimal otherValue) { return ((object)otherValue == (object)null) ? CBORObject.Null : @@ -883,7 +883,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.String)"]/*'/> public static CBORObject FromObject(string strValue) { if (strValue == null) { return CBORObject.Null; @@ -896,52 +896,52 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Int32)"]/*'/> public static CBORObject FromObject(int value) { return (value >= 0 && value < 24) ? valueFixedObjects[value] : FromObject((long)value); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Int16)"]/*'/> public static CBORObject FromObject(short value) { return (value >= 0 && value < 24) ? valueFixedObjects[value] : FromObject((long)value); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Char)"]/*'/> public static CBORObject FromObject(char value) { char[] valueChar = { value }; return FromObject(new String(valueChar)); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Boolean)"]/*'/> public static CBORObject FromObject(bool value) { return value ? CBORObject.True : CBORObject.False; } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Byte)"]/*'/> public static CBORObject FromObject(byte value) { return FromObject(((int)value) & 0xff); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Single)"]/*'/> public static CBORObject FromObject(float value) { return new CBORObject(CBORObjectTypeSingle, value); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Double)"]/*'/> public static CBORObject FromObject(double value) { return new CBORObject(CBORObjectTypeDouble, value); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Byte[])"]/*'/> public static CBORObject FromObject(byte[] bytes) { if (bytes == null) { return CBORObject.Null; @@ -952,7 +952,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(CBORObject[])"]/*'/> public static CBORObject FromObject(CBORObject[] array) { if (array == null) { return CBORObject.Null; @@ -965,7 +965,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Int32[])"]/*'/> public static CBORObject FromObject(int[] array) { if (array == null) { return CBORObject.Null; @@ -978,7 +978,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Int64[])"]/*'/> public static CBORObject FromObject(long[] array) { if (array == null) { return CBORObject.Null; @@ -992,7 +992,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject``1(System.Collections.Generic.IList{``0})"]/*'/> public static CBORObject FromObject(IList value) { if (value == null) { return CBORObject.Null; @@ -1005,7 +1005,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject``1(System.Collections.Generic.IEnumerable{``0})"]/*'/> public static CBORObject FromObject(IEnumerable value) { if (value == null) { return CBORObject.Null; @@ -1018,7 +1018,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject``2(System.Collections.Generic.IDictionary{``0,``1})"]/*'/> public static CBORObject FromObject(IDictionary dic) { if (dic == null) { @@ -1034,13 +1034,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Object)"]/*'/> public static CBORObject FromObject(object obj) { return FromObject(obj, PODOptions.Default); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Object,PODOptions)"]/*'/> public static CBORObject FromObject( object obj, PODOptions options) { @@ -1178,7 +1178,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObjectAndTag(System.Object,PeterO.BigInteger)"]/*'/> [Obsolete("Use the EInteger version instead.")] public static CBORObject FromObjectAndTag( object valueOb, @@ -1190,7 +1190,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.FromObjectAndTag(System.Object,PeterO.Numbers.EInteger)"]/*'/> public static CBORObject FromObjectAndTag( object valueOb, EInteger bigintTag) { @@ -1235,7 +1235,7 @@ public sealed partial class CBORObject : IComparable, #pragma warning disable 612 #pragma warning disable 618 /// + /// path='docs/doc[@name="M:CBORObject.FromObjectAndTag(System.Object,System.Int32)"]/*'/> public static CBORObject FromObjectAndTag( object valueObValue, int smallTag) { @@ -1252,7 +1252,7 @@ public sealed partial class CBORObject : IComparable, #pragma warning restore 612 /// + /// path='docs/doc[@name="M:CBORObject.FromSimpleValue(System.Int32)"]/*'/> public static CBORObject FromSimpleValue(int simpleValue) { if (simpleValue < 0) { throw new ArgumentException("simpleValue (" + simpleValue + @@ -1275,25 +1275,25 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Multiply(CBORObject,CBORObject)"]/*'/> public static CBORObject Multiply(CBORObject first, CBORObject second) { return CBORObjectMath.Multiply(first, second); } /// + /// path='docs/doc[@name="M:CBORObject.NewArray"]/*'/> public static CBORObject NewArray() { return new CBORObject(CBORObjectTypeArray, new List()); } /// + /// path='docs/doc[@name="M:CBORObject.NewMap"]/*'/> public static CBORObject NewMap() { return FromObject(new Dictionary()); } /// + /// path='docs/doc[@name="M:CBORObject.Read(System.IO.Stream)"]/*'/> public static CBORObject Read(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1307,7 +1307,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Read(System.IO.Stream,CBOREncodeOptions)"]/*'/> public static CBORObject Read(Stream stream, CBOREncodeOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); @@ -1324,13 +1324,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ReadJSON(System.IO.Stream)"]/*'/> public static CBORObject ReadJSON(Stream stream) { return ReadJSON(stream, new CBOREncodeOptions(true, true)); } /// + /// path='docs/doc[@name="M:CBORObject.ReadJSON(System.IO.Stream,CBOREncodeOptions)"]/*'/> public static CBORObject ReadJSON( Stream stream, CBOREncodeOptions options) { @@ -1363,19 +1363,19 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Remainder(CBORObject,CBORObject)"]/*'/> public static CBORObject Remainder(CBORObject first, CBORObject second) { return CBORObjectMath.Remainder(first, second); } /// + /// path='docs/doc[@name="M:CBORObject.Subtract(CBORObject,CBORObject)"]/*'/> public static CBORObject Subtract(CBORObject first, CBORObject second) { return CBORObjectMath.Subtract(first, second); } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.String,System.IO.Stream)"]/*'/> public static void Write(string str, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1385,7 +1385,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.String,System.IO.Stream,CBOREncodeOptions)"]/*'/> public static void Write( string str, Stream stream, @@ -1412,7 +1412,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.ExtendedFloat,System.IO.Stream)"]/*'/> [Obsolete("Pass an EFloat to the Write method instead.")] public static void Write(ExtendedFloat bignum, Stream stream) { if (stream == null) { @@ -1426,7 +1426,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.Numbers.EFloat,System.IO.Stream)"]/*'/> public static void Write(EFloat bignum, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1459,7 +1459,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.ExtendedRational,System.IO.Stream)"]/*'/> [Obsolete("Pass an ERational to the Write method instead.")] public static void Write(ExtendedRational rational, Stream stream) { if (stream == null) { @@ -1473,7 +1473,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.Numbers.ERational,System.IO.Stream)"]/*'/> public static void Write(ERational rational, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1498,7 +1498,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.ExtendedDecimal,System.IO.Stream)"]/*'/> [Obsolete("Pass an EDecimal to the Write method instead.")] public static void Write(ExtendedDecimal bignum, Stream stream) { if (stream == null) { @@ -1512,7 +1512,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.Numbers.EDecimal,System.IO.Stream)"]/*'/> public static void Write(EDecimal bignum, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1545,7 +1545,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.BigInteger,System.IO.Stream)"]/*'/> [Obsolete("Pass an EInteger to the Write method instead.")] public static void Write(BigInteger bigint, Stream stream) { if (stream == null) { @@ -1559,7 +1559,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(PeterO.Numbers.EInteger,System.IO.Stream)"]/*'/> public static void Write(EInteger bigint, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1651,7 +1651,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Int64,System.IO.Stream)"]/*'/> public static void Write(long value, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1666,7 +1666,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Int32,System.IO.Stream)"]/*'/> public static void Write(int value, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1695,13 +1695,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Int16,System.IO.Stream)"]/*'/> public static void Write(short value, Stream stream) { Write((long)value, stream); } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Char,System.IO.Stream)"]/*'/> public static void Write(char value, Stream stream) { if (value >= 0xd800 && value < 0xe000) { throw new ArgumentException("Value is a surrogate code point."); @@ -1711,7 +1711,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Boolean,System.IO.Stream)"]/*'/> public static void Write(bool value, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1720,7 +1720,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Byte,System.IO.Stream)"]/*'/> public static void Write(byte value, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1734,7 +1734,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Single,System.IO.Stream)"]/*'/> public static void Write(float value, Stream s) { if (s == null) { throw new ArgumentNullException(nameof(s)); @@ -1747,7 +1747,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Double,System.IO.Stream)"]/*'/> public static void Write(double value, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1765,7 +1765,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(CBORObject,System.IO.Stream)"]/*'/> public static void Write(CBORObject value, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -1778,14 +1778,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Object,System.IO.Stream)"]/*'/> public static void Write(object objValue, Stream stream) { // TODO: Use CBOREncodeOptions.Default in future versions Write(objValue, stream, new CBOREncodeOptions(true, true)); } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.Object,System.IO.Stream,CBOREncodeOptions)"]/*'/> public static void Write( object objValue, Stream output, @@ -1828,7 +1828,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.WriteJSON(System.Object,System.IO.Stream)"]/*'/> public static void WriteJSON(object obj, Stream outputStream) { if (obj == null) { outputStream.Write(ValueNullBytes, 0, ValueNullBytes.Length); @@ -1846,7 +1846,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Abs"]/*'/> public CBORObject Abs() { ICBORNumber cn = NumberInterfaces[this.ItemType]; if (cn == null) { @@ -1872,7 +1872,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Add(System.Object,System.Object)"]/*'/> public CBORObject Add(object key, object valueOb) { if (this.ItemType == CBORObjectTypeMap) { CBORObject mapKey; @@ -1901,7 +1901,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Add(CBORObject)"]/*'/> public CBORObject Add(CBORObject obj) { if (this.ItemType == CBORObjectTypeArray) { IList list = this.AsList(); @@ -1912,7 +1912,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Add(System.Object)"]/*'/> public CBORObject Add(object obj) { if (this.ItemType == CBORObjectTypeArray) { IList list = this.AsList(); @@ -1923,7 +1923,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsBigInteger"]/*'/> [Obsolete("Use the AsEInteger method instead.")] public BigInteger AsBigInteger() { ICBORNumber cn = NumberInterfaces[this.ItemType]; @@ -1936,7 +1936,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsEInteger"]/*'/> public EInteger AsEInteger() { // TODO: Consider returning null if this object is null // in next major version @@ -1948,19 +1948,19 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsBoolean"]/*'/> public bool AsBoolean() { return !this.IsFalse && !this.IsNull && !this.IsUndefined; } /// + /// path='docs/doc[@name="M:CBORObject.AsByte"]/*'/> public byte AsByte() { return (byte)this.AsInt32(0, 255); } /// + /// path='docs/doc[@name="M:CBORObject.AsDouble"]/*'/> public double AsDouble() { ICBORNumber cn = NumberInterfaces[this.ItemType]; if (cn == null) { @@ -1970,14 +1970,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsExtendedDecimal"]/*'/> [Obsolete("Use AsEDecimal instead.")] public ExtendedDecimal AsExtendedDecimal() { return ExtendedDecimal.FromString(this.AsEDecimal().ToString()); } /// + /// path='docs/doc[@name="M:CBORObject.AsEDecimal"]/*'/> public EDecimal AsEDecimal() { // TODO: Consider returning null if this object is null // in next major version @@ -1989,14 +1989,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsExtendedFloat"]/*'/> [Obsolete("Use AsEFloat instead.")] public ExtendedFloat AsExtendedFloat() { return ExtendedFloat.FromString(this.AsEFloat().ToString()); } /// + /// path='docs/doc[@name="M:CBORObject.AsEFloat"]/*'/> public EFloat AsEFloat() { // TODO: Consider returning null if this object is null // in next major version @@ -2008,14 +2008,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsExtendedRational"]/*'/> [Obsolete("Use AsERational instead.")] public ExtendedRational AsExtendedRational() { return PropertyMap.ToLegacy(this.AsERational()); } /// + /// path='docs/doc[@name="M:CBORObject.AsERational"]/*'/> // TODO: Consider returning null if this object is null // in next major version public ERational AsERational() { @@ -2027,19 +2027,19 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsInt16"]/*'/> public short AsInt16() { return (short)this.AsInt32(Int16.MinValue, Int16.MaxValue); } /// + /// path='docs/doc[@name="M:CBORObject.AsInt32"]/*'/> public int AsInt32() { return this.AsInt32(Int32.MinValue, Int32.MaxValue); } /// + /// path='docs/doc[@name="M:CBORObject.AsInt64"]/*'/> public long AsInt64() { ICBORNumber cn = NumberInterfaces[this.ItemType]; if (cn == null) { @@ -2049,7 +2049,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsSingle"]/*'/> public float AsSingle() { ICBORNumber cn = NumberInterfaces[this.ItemType]; if (cn == null) { @@ -2059,7 +2059,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.AsString"]/*'/> public string AsString() { // TODO: Consider returning null if this object is null // in next major version @@ -2073,14 +2073,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.CanFitInDouble"]/*'/> public bool CanFitInDouble() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return (cn != null) && cn.CanFitInDouble(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.CanFitInInt32"]/*'/> public bool CanFitInInt32() { if (!this.CanFitInInt64()) { return false; @@ -2090,35 +2090,35 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.CanFitInInt64"]/*'/> public bool CanFitInInt64() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return (cn != null) && cn.CanFitInInt64(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.CanFitInSingle"]/*'/> public bool CanFitInSingle() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return (cn != null) && cn.CanFitInSingle(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.CanTruncatedIntFitInInt32"]/*'/> public bool CanTruncatedIntFitInInt32() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return (cn != null) && cn.CanTruncatedIntFitInInt32(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.CanTruncatedIntFitInInt64"]/*'/> public bool CanTruncatedIntFitInInt64() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return cn != null && cn.CanTruncatedIntFitInInt64(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.CompareTo(CBORObject)"]/*'/> public int CompareTo(CBORObject other) { if (other == null) { return 1; @@ -2335,14 +2335,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.CompareToIgnoreTags(CBORObject)"]/*'/> public int CompareToIgnoreTags(CBORObject other) { return (other == null) ? 1 : ((this == other) ? 0 : this.Untag().CompareTo(other.Untag())); } /// + /// path='docs/doc[@name="M:CBORObject.ContainsKey(CBORObject)"]/*'/> public bool ContainsKey(CBORObject key) { if (key == null) { throw new ArgumentNullException(nameof(key)); @@ -2355,7 +2355,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ContainsKey(System.String)"]/*'/> public bool ContainsKey(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); @@ -2368,13 +2368,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.EncodeToBytes"]/*'/> public byte[] EncodeToBytes() { return this.EncodeToBytes(new CBOREncodeOptions(true, true)); } /// + /// path='docs/doc[@name="M:CBORObject.EncodeToBytes(CBOREncodeOptions)"]/*'/> public byte[] EncodeToBytes(CBOREncodeOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); @@ -2492,13 +2492,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Equals(System.Object)"]/*'/> public override bool Equals(object obj) { return this.Equals(obj as CBORObject); } /// + /// path='docs/doc[@name="M:CBORObject.Equals(CBORObject)"]/*'/> public bool Equals(CBORObject other) { var otherValue = other as CBORObject; if (otherValue == null) { @@ -2541,7 +2541,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.GetByteString"]/*'/> public byte[] GetByteString() { if (this.ItemType == CBORObjectTypeByteString) { return (byte[])this.ThisItem; @@ -2550,7 +2550,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.GetHashCode"]/*'/> public override int GetHashCode() { var hashCode = 651869431; unchecked { @@ -2603,7 +2603,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.GetTags"]/*'/> [Obsolete("Use the GetAllTags method instead.")] public BigInteger[] GetTags() { EInteger[] etags = this.GetAllTags(); @@ -2618,7 +2618,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.GetAllTags"]/*'/> public EInteger[] GetAllTags() { if (!this.IsTagged) { return ValueEmptyTags; @@ -2639,7 +2639,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.HasTag(System.Int32)"]/*'/> public bool HasMostOuterTag(int tagValue) { if (tagValue < 0) { throw new ArgumentException("tagValue (" + tagValue + @@ -2649,7 +2649,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.HasTag(PeterO.BigInteger)"]/*'/> public bool HasMostOuterTag(EInteger bigTagValue) { if (bigTagValue == null) { throw new ArgumentNullException(nameof(bigTagValue)); @@ -2662,7 +2662,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.HasTag(System.Int32)"]/*'/> public bool HasTag(int tagValue) { if (tagValue < 0) { throw new ArgumentException("tagValue (" + tagValue + @@ -2686,7 +2686,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.HasTag(PeterO.BigInteger)"]/*'/> [Obsolete("Use the EInteger version of this method.")] public bool HasTag(BigInteger bigTagValue) { if (bigTagValue == null) { @@ -2698,7 +2698,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.HasTag(PeterO.Numbers.EInteger)"]/*'/> public bool HasTag(EInteger bigTagValue) { if (bigTagValue == null) { throw new ArgumentNullException(nameof(bigTagValue)); @@ -2716,7 +2716,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Insert(System.Int32,System.Object)"]/*'/> public CBORObject Insert(int index, object valueOb) { if (this.ItemType == CBORObjectTypeArray) { CBORObject mapValue; @@ -2738,35 +2738,35 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.IsInfinity"]/*'/> public bool IsInfinity() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return cn != null && cn.IsInfinity(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.IsNaN"]/*'/> public bool IsNaN() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return cn != null && cn.IsNaN(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.IsNegativeInfinity"]/*'/> public bool IsNegativeInfinity() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return cn != null && cn.IsNegativeInfinity(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.IsPositiveInfinity"]/*'/> public bool IsPositiveInfinity() { ICBORNumber cn = NumberInterfaces[this.ItemType]; return cn != null && cn.IsPositiveInfinity(this.ThisItem); } /// + /// path='docs/doc[@name="M:CBORObject.Negate"]/*'/> public CBORObject Negate() { ICBORNumber cn = NumberInterfaces[this.ItemType]; if (cn == null) { @@ -2788,7 +2788,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Clear"]/*'/> public void Clear() { if (this.ItemType == CBORObjectTypeArray) { IList list = this.AsList(); @@ -2802,7 +2802,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Remove(System.Object)"]/*'/> public bool Remove(object obj) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); @@ -2812,7 +2812,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.RemoveAt(System.Int32)"]/*'/> public bool RemoveAt(int index) { if (this.ItemType != CBORObjectTypeArray) { throw new InvalidOperationException("Not an array"); @@ -2826,7 +2826,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Remove(CBORObject)"]/*'/> public bool Remove(CBORObject obj) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); @@ -2848,7 +2848,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Set(System.Object,System.Object)"]/*'/> public CBORObject Set(object key, object valueOb) { if (this.ItemType == CBORObjectTypeMap) { CBORObject mapKey; @@ -2878,13 +2878,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ToJSONString"]/*'/> public string ToJSONString() { return this.ToJSONString(JSONOptions.Default); } /// + /// path='docs/doc[@name="M:CBORObject.ToJSONString(JSONOptions)"]/*'/> public string ToJSONString(JSONOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); @@ -2911,7 +2911,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.ToString"]/*'/> public override string ToString() { StringBuilder sb = null; string simvalue = null; @@ -3079,7 +3079,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.Untag"]/*'/> public CBORObject Untag() { CBORObject curobject = this; while (curobject.itemtypeValue == CBORObjectTypeTagged) { @@ -3089,14 +3089,14 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.UntagOne"]/*'/> public CBORObject UntagOne() { return (this.itemtypeValue == CBORObjectTypeTagged) ? ((CBORObject)this.itemValue) : this; } /// + /// path='docs/doc[@name="M:CBORObject.WriteJSONTo(System.IO.Stream)"]/*'/> public void WriteJSONTo(Stream outputStream) { if (outputStream == null) { throw new ArgumentNullException(nameof(outputStream)); @@ -3108,7 +3108,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.WriteJSONTo(System.IO.Stream,JSONOptions)"]/*'/> public void WriteJSONTo(Stream outputStream, JSONOptions options) { if (outputStream == null) { throw new ArgumentNullException(nameof(outputStream)); @@ -3123,7 +3123,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.WriteValue(System.IO.Stream,System.Int32,System.Int64)"]/*'/> public static int WriteValue( Stream outputStream, int majorType, @@ -3164,7 +3164,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.WriteValue(System.IO.Stream,System.Int32,System.Int32)"]/*'/> public static int WriteValue( Stream outputStream, int majorType, @@ -3205,7 +3205,7 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.WriteValue(System.IO.Stream,System.Int32,PeterO.Numbers.EInteger)"]/*'/> public static int WriteValue( Stream outputStream, int majorType, @@ -3253,13 +3253,13 @@ public sealed partial class CBORObject : IComparable, } /// + /// path='docs/doc[@name="M:CBORObject.WriteTo(System.IO.Stream)"]/*'/> public void WriteTo(Stream stream) { this.WriteTo(stream, new CBOREncodeOptions(true, true)); } /// + /// path='docs/doc[@name="M:CBORObject.WriteTo(System.IO.Stream,CBOREncodeOptions)"]/*'/> public void WriteTo(Stream stream, CBOREncodeOptions options) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); @@ -4275,7 +4275,7 @@ private sealed class ConverterInfo { private object toObject; /// + /// path='docs/doc[@name="P:CBORObject.ConverterInfo.ToObject"]/*'/> public object ToObject { get { return this.toObject; @@ -4289,7 +4289,7 @@ private sealed class ConverterInfo { private object converter; /// + /// path='docs/doc[@name="P:CBORObject.ConverterInfo.Converter"]/*'/> public object Converter { get { return this.converter; diff --git a/CBOR/PeterO/Cbor/CBORObjectExtra.cs b/CBOR/PeterO/Cbor/CBORObjectExtra.cs index a7f68a99..39c7fc8d 100644 --- a/CBOR/PeterO/Cbor/CBORObjectExtra.cs +++ b/CBOR/PeterO/Cbor/CBORObjectExtra.cs @@ -15,7 +15,7 @@ namespace PeterO.Cbor { // are specific to the .NET framework. public sealed partial class CBORObject { /// + /// path='docs/doc[@name="M:CBORObject.AsUInt16"]/*'/> [CLSCompliant(false)] public ushort AsUInt16() { int v = this.AsInt32(); @@ -26,7 +26,7 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.AsUInt32"]/*'/> [CLSCompliant(false)] public uint AsUInt32() { ulong v = this.AsUInt64(); @@ -37,7 +37,7 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.AsSByte"]/*'/> [CLSCompliant(false)] public sbyte AsSByte() { int v = this.AsInt32(); @@ -48,7 +48,7 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.WriteValue(System.IO.Stream,System.Int32,System.UInt32)"]/*'/> [CLSCompliant(false)] public static int WriteValue( Stream outputStream, @@ -61,7 +61,7 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.WriteValue(System.IO.Stream,System.Int32,System.UInt64)"]/*'/> [CLSCompliant(false)] public static int WriteValue( Stream outputStream, @@ -110,7 +110,7 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.AsDecimal"]/*'/> [CLSCompliant(false)] public decimal AsDecimal() { return (this.ItemType == CBORObjectTypeInteger) ? @@ -121,7 +121,7 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.AsUInt64"]/*'/> [CLSCompliant(false)] public ulong AsUInt64() { ICBORNumber cn = NumberInterfaces[this.ItemType]; @@ -136,14 +136,14 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.SByte,System.IO.Stream)"]/*'/> [CLSCompliant(false)] public static void Write(sbyte value, Stream stream) { Write((long)value, stream); } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.UInt64,System.IO.Stream)"]/*'/> [CLSCompliant(false)] public static void Write(ulong value, Stream stream) { if (stream == null) { @@ -165,27 +165,27 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.Decimal)"]/*'/> public static CBORObject FromObject(decimal value) { return FromObject((EDecimal)value); } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.UInt32,System.IO.Stream)"]/*'/> [CLSCompliant(false)] public static void Write(uint value, Stream stream) { Write((ulong)value, stream); } /// + /// path='docs/doc[@name="M:CBORObject.Write(System.UInt16,System.IO.Stream)"]/*'/> [CLSCompliant(false)] public static void Write(ushort value, Stream stream) { Write((ulong)value, stream); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.SByte)"]/*'/> [CLSCompliant(false)] public static CBORObject FromObject(sbyte value) { return FromObject((long)value); @@ -207,83 +207,83 @@ public sealed partial class CBORObject { } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.UInt64)"]/*'/> [CLSCompliant(false)] public static CBORObject FromObject(ulong value) { return CBORObject.FromObject(UInt64ToEInteger(value)); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.UInt32)"]/*'/> [CLSCompliant(false)] public static CBORObject FromObject(uint value) { return FromObject((long)(Int64)value); } /// + /// path='docs/doc[@name="M:CBORObject.FromObject(System.UInt16)"]/*'/> [CLSCompliant(false)] public static CBORObject FromObject(ushort value) { return FromObject((long)(Int64)value); } /// + /// path='docs/doc[@name="M:CBORObject.FromObjectAndTag(System.Object,System.UInt64)"]/*'/> [CLSCompliant(false)] public static CBORObject FromObjectAndTag(Object o, ulong tag) { return FromObjectAndTag(o, UInt64ToEInteger(tag)); } /// + /// path='docs/doc[@name="M:CBORObject.ToObject``1"]/*'/> public T ToObject() { return (T)this.ToObject(typeof(T)); } /// + /// path='docs/doc[@name="M:CBORObject.ToObject``1(CBORTypeMapper)"]/*'/> public T ToObject(CBORTypeMapper mapper) { return (T)this.ToObject(typeof(T), mapper); } /// + /// path='docs/doc[@name="M:CBORObject.ToObject``1(PODOptions)"]/*'/> public T ToObject(PODOptions options) { return (T)this.ToObject(typeof(T), options); } /// + /// path='docs/doc[@name="M:CBORObject.ToObject``1(CBORTypeMapper,PODOptions)"]/*'/> public T ToObject(CBORTypeMapper mapper, PODOptions options) { return (T)this.ToObject(typeof(T), mapper, options); } /// + /// path='docs/doc[@name="M:CBORObject.op_Addition(CBORObject,CBORObject)"]/*'/> public static CBORObject operator +(CBORObject a, CBORObject b) { return Addition(a, b); } /// + /// path='docs/doc[@name="M:CBORObject.op_Subtraction(CBORObject,CBORObject)"]/*'/> public static CBORObject operator -(CBORObject a, CBORObject b) { return Subtract(a, b); } /// + /// path='docs/doc[@name="M:CBORObject.op_Multiply(CBORObject,CBORObject)"]/*'/> public static CBORObject operator *(CBORObject a, CBORObject b) { return Multiply(a, b); } /// + /// path='docs/doc[@name="M:CBORObject.op_Division(CBORObject,CBORObject)"]/*'/> public static CBORObject operator /(CBORObject a, CBORObject b) { return Divide(a, b); } /// + /// path='docs/doc[@name="M:CBORObject.op_Modulus(CBORObject,CBORObject)"]/*'/> public static CBORObject operator %(CBORObject a, CBORObject b) { return Remainder(a, b); } diff --git a/CBOR/PeterO/Cbor/CBORObjectMath.cs b/CBOR/PeterO/Cbor/CBORObjectMath.cs index 57d20dfd..2fd773f4 100644 --- a/CBOR/PeterO/Cbor/CBORObjectMath.cs +++ b/CBOR/PeterO/Cbor/CBORObjectMath.cs @@ -11,7 +11,7 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORObjectMath"]/*'/> internal static class CBORObjectMath { public static CBORObject Addition(CBORObject a, CBORObject b) { if (a == null) { diff --git a/CBOR/PeterO/Cbor/CBORTag3.cs b/CBOR/PeterO/Cbor/CBORTag3.cs index d2a6d640..1ef3b061 100644 --- a/CBOR/PeterO/Cbor/CBORTag3.cs +++ b/CBOR/PeterO/Cbor/CBORTag3.cs @@ -9,7 +9,7 @@ #pragma warning disable 618 namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORTag3"]/*'/> internal class CBORTag3 : ICBORTag { public CBORTypeFilter GetTypeFilter() { diff --git a/CBOR/PeterO/Cbor/CBORTag37.cs b/CBOR/PeterO/Cbor/CBORTag37.cs index 927196c4..2b452edf 100644 --- a/CBOR/PeterO/Cbor/CBORTag37.cs +++ b/CBOR/PeterO/Cbor/CBORTag37.cs @@ -32,7 +32,7 @@ internal class CBORTag37 : ICBORTag, ICBORToFromConverter } /// + /// path='docs/doc[@name="M:CBORTag37.ToCBORObject(System.Guid)"]/*'/> public CBORObject ToCBORObject(Guid obj) { byte[] bytes = PropertyMap.UUIDToBytes(obj); return CBORObject.FromObjectAndTag(bytes, (int)37); diff --git a/CBOR/PeterO/Cbor/CBORTagGenericString.cs b/CBOR/PeterO/Cbor/CBORTagGenericString.cs index 6b38a39d..7ab37394 100644 --- a/CBOR/PeterO/Cbor/CBORTagGenericString.cs +++ b/CBOR/PeterO/Cbor/CBORTagGenericString.cs @@ -10,7 +10,7 @@ #pragma warning disable 618 namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORTagGenericString"]/*'/> internal class CBORTagGenericString : ICBORTag { public CBORTypeFilter GetTypeFilter() { diff --git a/CBOR/PeterO/Cbor/CBORType.cs b/CBOR/PeterO/Cbor/CBORType.cs index 25559c3b..62570e20 100644 --- a/CBOR/PeterO/Cbor/CBORType.cs +++ b/CBOR/PeterO/Cbor/CBORType.cs @@ -9,34 +9,34 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORType"]/*'/> public enum CBORType { /// + /// path='docs/doc[@name="F:CBORType.Number"]/*'/> Number, /// + /// path='docs/doc[@name="F:CBORType.Boolean"]/*'/> Boolean, /// + /// path='docs/doc[@name="F:CBORType.SimpleValue"]/*'/> SimpleValue, /// + /// path='docs/doc[@name="F:CBORType.ByteString"]/*'/> ByteString, /// + /// path='docs/doc[@name="F:CBORType.TextString"]/*'/> TextString, /// + /// path='docs/doc[@name="F:CBORType.Array"]/*'/> Array, /// + /// path='docs/doc[@name="F:CBORType.Map"]/*'/> Map } } diff --git a/CBOR/PeterO/Cbor/CBORTypeFilter.cs b/CBOR/PeterO/Cbor/CBORTypeFilter.cs index c3cf5768..60cf5f9f 100644 --- a/CBOR/PeterO/Cbor/CBORTypeFilter.cs +++ b/CBOR/PeterO/Cbor/CBORTypeFilter.cs @@ -11,34 +11,34 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORTypeFilter"]/*'/> [Obsolete("May be removed without replacement.")] public sealed class CBORTypeFilter { /// + /// path='docs/doc[@name="F:CBORTypeFilter.Any"]/*'/> public static readonly CBORTypeFilter Any = new CBORTypeFilter().WithAny(); /// + /// path='docs/doc[@name="F:CBORTypeFilter.ByteString"]/*'/> public static readonly CBORTypeFilter ByteString = new CBORTypeFilter().WithByteString(); /// + /// path='docs/doc[@name="F:CBORTypeFilter.NegativeInteger"]/*'/> public static readonly CBORTypeFilter NegativeInteger = new CBORTypeFilter().WithNegativeInteger(); /// + /// path='docs/doc[@name="F:CBORTypeFilter.None"]/*'/> public static readonly CBORTypeFilter None = new CBORTypeFilter(); /// + /// path='docs/doc[@name="F:CBORTypeFilter.TextString"]/*'/> public static readonly CBORTypeFilter TextString = new CBORTypeFilter().WithTextString(); /// + /// path='docs/doc[@name="F:CBORTypeFilter.UnsignedInteger"]/*'/> public static readonly CBORTypeFilter UnsignedInteger = new CBORTypeFilter().WithUnsignedInteger(); @@ -52,7 +52,7 @@ public sealed class CBORTypeFilter { private int types; /// + /// path='docs/doc[@name="M:CBORTypeFilter.ArrayIndexAllowed(System.Int32)"]/*'/> public bool ArrayIndexAllowed(int index) { return (this.types & (1 << 4)) != 0 && index >= 0 && (this.anyArrayLength || @@ -61,7 +61,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.ArrayLengthMatches(System.Int32)"]/*'/> public bool ArrayLengthMatches(int length) { return (this.types & (1 << 4)) != 0 && (this.anyArrayLength || (this.arrayMinLength ? this.arrayLength >= length : @@ -69,7 +69,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.ArrayLengthMatches(System.Int64)"]/*'/> public bool ArrayLengthMatches(long length) { return (this.types & (1 << 4)) != 0 && (this.anyArrayLength || (this.arrayMinLength ? this.arrayLength >= length : @@ -77,7 +77,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.ArrayLengthMatches(PeterO.Numbers.EInteger)"]/*'/> public bool ArrayLengthMatches(EInteger bigLength) { if (bigLength == null) { throw new ArgumentNullException(nameof(bigLength)); @@ -90,7 +90,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.GetSubFilter(System.Int32)"]/*'/> public CBORTypeFilter GetSubFilter(int index) { if (this.anyArrayLength || this.any) { return Any; @@ -114,7 +114,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.GetSubFilter(System.Int64)"]/*'/> public CBORTypeFilter GetSubFilter(long index) { if (this.anyArrayLength || this.any) { return Any; @@ -140,7 +140,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.MajorTypeMatches(System.Int32)"]/*'/> public bool MajorTypeMatches(int type) { #if DEBUG if (type < 0) { @@ -155,25 +155,25 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.NonFPSimpleValueAllowed"]/*'/> public bool NonFPSimpleValueAllowed() { return this.MajorTypeMatches(7) && !this.floatingpoint; } /// + /// path='docs/doc[@name="M:CBORTypeFilter.TagAllowed(System.Int32)"]/*'/> public bool TagAllowed(int tag) { return this.any || this.TagAllowed((EInteger)tag); } /// + /// path='docs/doc[@name="M:CBORTypeFilter.TagAllowed(System.Int64)"]/*'/> public bool TagAllowed(long longTag) { return this.any || this.TagAllowed((EInteger)longTag); } /// + /// path='docs/doc[@name="M:CBORTypeFilter.TagAllowed(PeterO.Numbers.EInteger)"]/*'/> public bool TagAllowed(EInteger bigTag) { if (bigTag == null) { throw new ArgumentNullException(nameof(bigTag)); @@ -199,7 +199,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithArrayAnyLength"]/*'/> public CBORTypeFilter WithArrayAnyLength() { if (this.any) { return this; @@ -219,7 +219,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithArrayExactLength(System.Int32,CBORTypeFilter[])"]/*'/> public CBORTypeFilter WithArrayExactLength( int arrayLength, params CBORTypeFilter[] elements) { @@ -247,7 +247,7 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithArrayMinLength(System.Int32,CBORTypeFilter[])"]/*'/> public CBORTypeFilter WithArrayMinLength( int arrayLength, params CBORTypeFilter[] elements) { @@ -275,13 +275,13 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithByteString"]/*'/> public CBORTypeFilter WithByteString() { return this.WithType(2).WithTags(25); } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithFloatingPoint"]/*'/> public CBORTypeFilter WithFloatingPoint() { if (this.any) { return this; @@ -293,19 +293,19 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithMap"]/*'/> public CBORTypeFilter WithMap() { return this.WithType(5); } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithNegativeInteger"]/*'/> public CBORTypeFilter WithNegativeInteger() { return this.WithType(1); } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithTags(System.Int32[])"]/*'/> public CBORTypeFilter WithTags(params int[] tags) { if (this.any) { return this; @@ -352,13 +352,13 @@ public sealed class CBORTypeFilter { } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithTextString"]/*'/> public CBORTypeFilter WithTextString() { return this.WithType(3).WithTags(25); } /// + /// path='docs/doc[@name="M:CBORTypeFilter.WithUnsignedInteger"]/*'/> public CBORTypeFilter WithUnsignedInteger() { return this.WithType(0); } diff --git a/CBOR/PeterO/Cbor/CBORTypeMapper.cs b/CBOR/PeterO/Cbor/CBORTypeMapper.cs index 2986b841..5694f9f3 100644 --- a/CBOR/PeterO/Cbor/CBORTypeMapper.cs +++ b/CBOR/PeterO/Cbor/CBORTypeMapper.cs @@ -3,7 +3,7 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORTypeMapper"]/*'/> public sealed class CBORTypeMapper { private readonly IList typePrefixes; private readonly IList typeNames; @@ -98,7 +98,7 @@ public sealed class CBORTypeMapper { } /// + /// path='docs/doc[@name="M:CBORTypeMapper.FilterTypeName(System.String)"]/*'/> public bool FilterTypeName(string typeName) { if (String.IsNullOrEmpty(typeName)) { return false; @@ -118,7 +118,7 @@ public sealed class CBORTypeMapper { } /// + /// path='docs/doc[@name="M:CBORTypeMapper.AddTypePrefix(System.String)"]/*'/> public CBORTypeMapper AddTypePrefix(string prefix) { if (prefix == null) { throw new ArgumentNullException(nameof(prefix)); @@ -131,7 +131,7 @@ public sealed class CBORTypeMapper { } /// + /// path='docs/doc[@name="M:CBORTypeMapper.AddTypeName(System.String)"]/*'/> public CBORTypeMapper AddTypeName(string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); @@ -145,7 +145,7 @@ public sealed class CBORTypeMapper { internal sealed class ConverterInfo { /// + /// path='docs/doc[@name="P:CBORObject.ConverterInfo.ToObject"]/*'/> public object ToObject { get; set; } public object FromObject { get; set; } diff --git a/CBOR/PeterO/Cbor/CBORUtilities.cs b/CBOR/PeterO/Cbor/CBORUtilities.cs index d8b74f52..77478870 100644 --- a/CBOR/PeterO/Cbor/CBORUtilities.cs +++ b/CBOR/PeterO/Cbor/CBORUtilities.cs @@ -12,7 +12,7 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:CBORUtilities"]/*'/> internal static class CBORUtilities { private const string HexAlphabet = "0123456789ABCDEF"; diff --git a/CBOR/PeterO/Cbor/CBORUuidConverter.cs b/CBOR/PeterO/Cbor/CBORUuidConverter.cs index 6b75d65a..29b89c16 100644 --- a/CBOR/PeterO/Cbor/CBORUuidConverter.cs +++ b/CBOR/PeterO/Cbor/CBORUuidConverter.cs @@ -22,7 +22,7 @@ internal class CBORUuidConverter : ICBORToFromConverter } /// + /// path='docs/doc[@name="M:CBORTag37.ToCBORObject(System.Guid)"]/*'/> public CBORObject ToCBORObject(Guid obj) { byte[] bytes = PropertyMap.UUIDToBytes(obj); return CBORObject.FromObjectAndTag(bytes, (int)37); diff --git a/CBOR/PeterO/Cbor/CharacterReader.cs b/CBOR/PeterO/Cbor/CharacterReader.cs index fe9970cd..fe75a821 100644 --- a/CBOR/PeterO/Cbor/CharacterReader.cs +++ b/CBOR/PeterO/Cbor/CharacterReader.cs @@ -10,7 +10,7 @@ namespace PeterO.Cbor { // + // path='docs/doc[@name="T:CharacterReader"]/*'/> internal sealed class CharacterReader : ICharacterInput { private readonly int mode; private readonly bool errorThrow; @@ -23,18 +23,18 @@ internal sealed class CharacterReader : ICharacterInput { private ICharacterInput reader; // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.String)"]/*'/> public CharacterReader(string str) : this(str, false, false) { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.String,System.Boolean)"]/*'/> public CharacterReader(string str, bool skipByteOrderMark) : this(str, skipByteOrderMark, false) { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.String,System.Boolean,System.Boolean)"]/*'/> public CharacterReader( string str, bool skipByteOrderMark, @@ -53,13 +53,13 @@ internal sealed class CharacterReader : ICharacterInput { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.String,System.Int32,System.Int32)"]/*'/> public CharacterReader(string str, int offset, int length) : this(str, offset, length, false, false) { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.String,System.Int32,System.Int32,System.Boolean,System.Boolean)"]/*'/> public CharacterReader( string str, int offset, @@ -100,24 +100,24 @@ internal sealed class CharacterReader : ICharacterInput { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.IO.Stream)"]/*'/> public CharacterReader(Stream stream) : this(stream, 0, false) { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.IO.Stream,System.Int32,System.Boolean)"]/*'/> public CharacterReader(Stream stream, int mode, bool errorThrow) : this(stream, mode, errorThrow, false) { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.IO.Stream,System.Int32)"]/*'/> public CharacterReader(Stream stream, int mode) : this(stream, mode, false, false) { } // + // path='docs/doc[@name="M:CharacterReader.#ctor(System.IO.Stream,System.Int32,System.Boolean,System.Boolean)"]/*'/> public CharacterReader( Stream stream, int mode, @@ -139,7 +139,7 @@ private interface IByteReader { } // + // path='docs/doc[@name="M:CharacterReader.Read(System.Int32[],System.Int32,System.Int32)"]/*'/> public int Read(int[] chars, int index, int length) { if (chars == null) { throw new ArgumentNullException(nameof(chars)); @@ -177,7 +177,7 @@ private interface IByteReader { } // + // path='docs/doc[@name="M:CharacterReader.ReadChar"]/*'/> public int ReadChar() { if (this.reader != null) { return this.reader.ReadChar(); diff --git a/CBOR/PeterO/Cbor/FastInteger2.cs b/CBOR/PeterO/Cbor/FastInteger2.cs index 45982932..228a7c8e 100644 --- a/CBOR/PeterO/Cbor/FastInteger2.cs +++ b/CBOR/PeterO/Cbor/FastInteger2.cs @@ -348,7 +348,7 @@ private sealed class MutableNumber { } /// + /// path='docs/doc[@name="M:FastInteger2.Multiply(System.Int32)"]/*'/> internal FastInteger2 Multiply(int val) { if (val == 0) { this.smallValue = 0; @@ -401,7 +401,7 @@ private sealed class MutableNumber { } /// + /// path='docs/doc[@name="M:FastInteger2.Subtract(FastInteger2)"]/*'/> internal FastInteger2 Subtract(FastInteger2 val) { EInteger valValue; switch (this.integerMode) { @@ -448,7 +448,7 @@ private sealed class MutableNumber { } /// + /// path='docs/doc[@name="M:FastInteger2.SubtractInt(System.Int32)"]/*'/> internal FastInteger2 SubtractInt(int val) { if (val == Int32.MinValue) { return this.AddInt(Int32.MaxValue).AddInt(1); @@ -571,7 +571,7 @@ private sealed class MutableNumber { } /// + /// path='docs/doc[@name="P:FastInteger2.Sign"]/*'/> internal int Sign { get { switch (this.integerMode) { diff --git a/CBOR/PeterO/Cbor/ICBORConverter.cs b/CBOR/PeterO/Cbor/ICBORConverter.cs index 8bd72958..ac3fb98e 100644 --- a/CBOR/PeterO/Cbor/ICBORConverter.cs +++ b/CBOR/PeterO/Cbor/ICBORConverter.cs @@ -9,11 +9,11 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:ICBORConverter`1"]/*'/> public interface ICBORConverter { /// + /// path='docs/doc[@name="M:ICBORConverter`1.ToCBORObject(`0)"]/*'/> CBORObject ToCBORObject(T obj); } } diff --git a/CBOR/PeterO/Cbor/ICBORTag.cs b/CBOR/PeterO/Cbor/ICBORTag.cs index c19c829d..b9d5c007 100644 --- a/CBOR/PeterO/Cbor/ICBORTag.cs +++ b/CBOR/PeterO/Cbor/ICBORTag.cs @@ -9,16 +9,16 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:ICBORTag"]/*'/> [Obsolete("May be removed in the future without replacement. Not as useful as ICBORConverters and ICBORObjectConverters for FromObject and ToObject.")] public interface ICBORTag { /// + /// path='docs/doc[@name="M:ICBORTag.GetTypeFilter"]/*'/> CBORTypeFilter GetTypeFilter(); /// + /// path='docs/doc[@name="M:ICBORTag.ValidateObject(CBORObject)"]/*'/> CBORObject ValidateObject(CBORObject obj); } } diff --git a/CBOR/PeterO/Cbor/ICBORToFromConverter.cs b/CBOR/PeterO/Cbor/ICBORToFromConverter.cs index 2037d88e..d6800769 100644 --- a/CBOR/PeterO/Cbor/ICBORToFromConverter.cs +++ b/CBOR/PeterO/Cbor/ICBORToFromConverter.cs @@ -2,10 +2,10 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:ICBORObjectConverter`1"]/*'/> public interface ICBORToFromConverter : ICBORConverter { /// + /// path='docs/doc[@name="M:ICBORObjectConverter`1.FromCBORObject(CBORObject)"]/*'/> T FromCBORObject(CBORObject cbor); } } diff --git a/CBOR/PeterO/Cbor/ICharacterInput.cs b/CBOR/PeterO/Cbor/ICharacterInput.cs index 3d4a4c63..da5dcc75 100644 --- a/CBOR/PeterO/Cbor/ICharacterInput.cs +++ b/CBOR/PeterO/Cbor/ICharacterInput.cs @@ -2,14 +2,14 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:ICharacterInput"]/*'/> internal interface ICharacterInput { /// + /// path='docs/doc[@name="M:ICharacterInput.ReadChar"]/*'/> int ReadChar(); /// + /// path='docs/doc[@name="M:ICharacterInput.Read(System.Int32[],System.Int32,System.Int32)"]/*'/> int Read(int[] chars, int index, int length); } } diff --git a/CBOR/PeterO/Cbor/JSONOptions.cs b/CBOR/PeterO/Cbor/JSONOptions.cs index c6c0bb6a..93456e40 100644 --- a/CBOR/PeterO/Cbor/JSONOptions.cs +++ b/CBOR/PeterO/Cbor/JSONOptions.cs @@ -4,25 +4,25 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:JSONOptions"]/*'/> public sealed class JSONOptions { /// + /// path='docs/doc[@name="M:JSONOptions.#ctor"]/*'/> public JSONOptions() : this(false) { } /// + /// path='docs/doc[@name="M:JSONOptions.#ctor(System.Boolean)"]/*'/> public JSONOptions(bool base64Padding) { this.Base64Padding = base64Padding; } /// + /// path='docs/doc[@name="F:JSONOptions.Default"]/*'/> public static readonly JSONOptions Default = new JSONOptions(); /// + /// path='docs/doc[@name="P:JSONOptions.Base64Padding"]/*'/> public bool Base64Padding { get; private set; } } } diff --git a/CBOR/PeterO/Cbor/PODOptions.cs b/CBOR/PeterO/Cbor/PODOptions.cs index be6d88d1..9b823b6a 100644 --- a/CBOR/PeterO/Cbor/PODOptions.cs +++ b/CBOR/PeterO/Cbor/PODOptions.cs @@ -2,15 +2,15 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:PODOptions"]/*'/> public class PODOptions { /// + /// path='docs/doc[@name="M:PODOptions.#ctor"]/*'/> public PODOptions() : this(true, true) { } /// + /// path='docs/doc[@name="M:PODOptions.#ctor(System.Boolean,System.Boolean)"]/*'/> public PODOptions(bool removeIsPrefix, bool useCamelCase) { #pragma warning disable 618 this.RemoveIsPrefix = removeIsPrefix; @@ -19,16 +19,16 @@ public class PODOptions { } /// + /// path='docs/doc[@name="F:PODOptions.Default"]/*'/> public static readonly PODOptions Default = new PODOptions(); /// + /// path='docs/doc[@name="P:PODOptions.RemoveIsPrefix"]/*'/> [Obsolete("Property name conversion may change, making this property obsolete.")] public bool RemoveIsPrefix { get; private set; } /// + /// path='docs/doc[@name="P:PODOptions.UseCamelCase"]/*'/> public bool UseCamelCase { get; private set; } } } diff --git a/CBOR/PeterO/Cbor/StringRefs.cs b/CBOR/PeterO/Cbor/StringRefs.cs index d1f648af..b99fe89a 100644 --- a/CBOR/PeterO/Cbor/StringRefs.cs +++ b/CBOR/PeterO/Cbor/StringRefs.cs @@ -12,7 +12,7 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:StringRefs"]/*'/> internal class StringRefs { private readonly List> stack; diff --git a/CBOR/PeterO/Cbor/URIUtility.cs b/CBOR/PeterO/Cbor/URIUtility.cs index 05a8afd3..707b96c2 100644 --- a/CBOR/PeterO/Cbor/URIUtility.cs +++ b/CBOR/PeterO/Cbor/URIUtility.cs @@ -11,29 +11,29 @@ namespace PeterO.Cbor { /// + /// path='docs/doc[@name="T:URIUtility"]/*'/> internal static class URIUtility { /// + /// path='docs/doc[@name="T:URIUtility.ParseMode"]/*'/> internal enum ParseMode { /// + /// path='docs/doc[@name="F:URIUtility.ParseMode.IRIStrict"]/*'/> IRIStrict, /// + /// path='docs/doc[@name="F:URIUtility.ParseMode.URIStrict"]/*'/> URIStrict, /// + /// path='docs/doc[@name="F:URIUtility.ParseMode.IRILenient"]/*'/> IRILenient, /// + /// path='docs/doc[@name="F:URIUtility.ParseMode.URILenient"]/*'/> URILenient, /// + /// path='docs/doc[@name="F:URIUtility.ParseMode.IRISurrogateLenient"]/*'/> IRISurrogateLenient } @@ -113,7 +113,7 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.escapeURI(System.String,System.Int32)"]/*'/> public static string escapeURI(string s, int mode) { if (s == null) { return null; @@ -226,7 +226,7 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.hasScheme(System.String)"]/*'/> public static bool hasScheme(string refValue) { int[] segments = (refValue == null) ? null : splitIRI( refValue, @@ -237,7 +237,7 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.hasSchemeForURI(System.String)"]/*'/> public static bool hasSchemeForURI(string refValue) { int[] segments = (refValue == null) ? null : splitIRI( refValue, @@ -303,7 +303,7 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.isValidCurieReference(System.String,System.Int32,System.Int32)"]/*'/> public static bool isValidCurieReference(string s, int offset, int length) { if (s == null) { return false; @@ -782,13 +782,13 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.relativeResolve(System.String,System.String)"]/*'/> public static string relativeResolve(string refValue, string baseURI) { return relativeResolve(refValue, baseURI, ParseMode.IRIStrict); } /// + /// path='docs/doc[@name="M:URIUtility.relativeResolve(System.String,System.String,URIUtility.ParseMode)"]/*'/> public static string relativeResolve( string refValue, string baseURI, @@ -861,13 +861,13 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.splitIRI(System.String)"]/*'/> public static int[] splitIRI(string s) { return (s == null) ? null : splitIRI(s, 0, s.Length, ParseMode.IRIStrict); } /// + /// path='docs/doc[@name="M:URIUtility.splitIRI(System.String,System.Int32,System.Int32,URIUtility.ParseMode)"]/*'/> public static int[] splitIRI( string s, int offset, @@ -1121,7 +1121,7 @@ internal enum ParseMode { } /// + /// path='docs/doc[@name="M:URIUtility.splitIRI(System.String,URIUtility.ParseMode)"]/*'/> public static int[] splitIRI(string s, ParseMode parseMode) { return (s == null) ? null : splitIRI(s, 0, s.Length, parseMode); } diff --git a/CBOR/docs.xml b/CBOR/docs.xml index 62390469..0e3198d4 100644 --- a/CBOR/docs.xml +++ b/CBOR/docs.xml @@ -277,14 +277,14 @@ - + Contains methods useful for reading and writing data, with a focus on CBOR. - + Parses a number whose format follows the JSON specification. See #ParseJSONNumber(String, integersOnly, parseOnly) for more information. @@ -297,7 +297,7 @@ - + Parses a number whose format follows the JSON specification (RFC 8259). Roughly speaking, a valid number consists of an optional minus sign, one @@ -317,7 +317,7 @@ - + Parses a number whose format follows the JSON specification (RFC 8259). Roughly speaking, a valid number consists of an optional minus sign, one @@ -338,21 +338,21 @@ - + Specifies options for encoding and decoding CBOR objects. - + - Initializes a new instance of the class. + Initializes a new instance of the class. - + - Initializes a new instance of the class. + Initializes a new instance of the class. A value indicating whether to always encode strings with a definite-length encoding. A value indicating whether to disallow duplicate keys when reading CBOR @@ -360,16 +360,16 @@ - + - Initializes a new instance of the class.A value indicating whether to + Initializes a new instance of the class.A value indicating whether to always encode strings with a definite-length encoding.A value indicating whether to disallow duplicate keys when reading CBOR objects from a data stream.Either true or false . - + Gets a value indicating whether to disallow duplicate keys when reading CBOR objects from a data stream. Used only when decoding CBOR objects. @@ -378,7 +378,7 @@ - + Returns an options object containing the flags shared by this and another options object. @@ -389,7 +389,7 @@ - + Gets a value indicating whether CBOR objects are written out using the CTAP2 canonical CBOR encoding form. In this form, @@ -403,7 +403,7 @@ numbers. - + Default options for CBOR objects. Disallow duplicate keys, and always encode strings using definite-length encoding. These are recommended @@ -412,27 +412,27 @@ - + Disallow duplicate keys when reading CBOR objects from a data stream. Used only when decoding CBOR objects. Value: 2. - + Always encode strings with a definite-length encoding. Used only when encoding CBOR objects. Value: 1. - + No special options for encoding/decoding. Value: 0. - + Returns an options object containing the combined flags of this and another options object. @@ -443,7 +443,7 @@ - + Gets a value indicating whether to always encode strings with a definite-length encoding. @@ -452,36 +452,36 @@ - + Gets this options object's value. This options object's value. - + Exception thrown for errors involving CBOR data. - + - Initializes a new instance of the class. + Initializes a new instance of the class. - + - Initializes a new instance of the class. + Initializes a new instance of the class. The parameter is a text string. - + - Initializes a new instance of the class. Uses the given message and inner exception. + Initializes a new instance of the class. Uses the given message and inner exception. The parameter is a text string. @@ -491,7 +491,7 @@ - + Registers an object that converts objects of a given type to CBOR objects (called a CBOR converter). @@ -516,7 +516,7 @@ "Converter doesn't contain a proper ToCBORObject method". - + Represents an object in Concise Binary Object Representation (CBOR) and contains methods for reading and writing CBOR data. CBOR is defined in RFC @@ -624,14 +624,14 @@ - + Gets this object's absolute value.This object's absolute without its negative sign.This object's type is not a number type. - + Adds a new object to the end of this array. (Used to throw ArgumentNullException on a null reference, but now converts @@ -657,7 +657,7 @@ - + Converts an object to a CBOR object and adds it to the end of this array. @@ -680,7 +680,7 @@ - + Adds a new key and its value to this CBOR map, or adds the value if the key doesn't exist. @@ -695,7 +695,7 @@ unsupported type. - + Registers an object that converts objects of a given type to CBOR objects (called a CBOR converter).A Type object specifying the type that the @@ -707,7 +707,7 @@ proper ToCBORObject method". - + Registers an object that validates CBOR objects with new tags.An arbitrary-precision integer.The parameter is @@ -718,7 +718,7 @@ (2^64-1). - + Registers an object that validates CBOR objects with new tags.An arbitrary-precision integer.The parameter is @@ -729,7 +729,7 @@ (2^64-1). - + Finds the sum of two CBOR numbers.The parameter is a CBOR object.The parameter is a @@ -738,7 +738,7 @@ NaN). - + Converts this object to an arbitrary-precision integer. Fractional values are truncated to an integer.The closest big integer to this object.This object's @@ -747,7 +747,7 @@ infinity or not-a-number (NaN). - + Returns false if this object is False, Null, or Undefined; otherwise,true. @@ -755,7 +755,7 @@ - + Converts this object to a byte (0 to 255). Floating point values are truncated to an integer.The closest byte-sized integer to this object.This object's @@ -764,7 +764,7 @@ 255 when truncated to an integer). - + Converts this object to a .NET decimal. The closest big integer to this object. @@ -773,7 +773,7 @@ - + Converts this object to a 64-bit floating point number.The closest 64-bit floating point number to this object. @@ -783,7 +783,7 @@ type is not a number type. - + Converts this object to a decimal number.A decimal number for this object's value. If this object is a rational number with a nonterminating decimal expansion, @@ -792,7 +792,7 @@ CBORObject.Null. - + Converts this object to an arbitrary-precision binary floating point number.An arbitrary-precision binary floating point number for @@ -805,7 +805,7 @@ CBORObject.Null. - + Converts this object to an arbitrary-precision integer. Fractional values are truncated to an integer.The closest big integer to this object.This object's @@ -814,14 +814,14 @@ infinity or not-a-number (NaN). - + Converts this object to a rational number.A rational number for this object's value.This object's type is not a number type, including if this object is CBORObject.Null. - + Converts this object to a decimal number.A decimal number for this object's value. If this object is a rational number with a nonterminating decimal expansion, @@ -830,7 +830,7 @@ CBORObject.Null. - + Converts this object to an arbitrary-precision binary floating point number.An arbitrary-precision binary floating point number for @@ -843,14 +843,14 @@ CBORObject.Null. - + Converts this object to a rational number.A rational number for this object's value.This object's type is not a number type, including if this object is CBORObject.Null. - + Converts this object to a 16-bit signed integer. Floating point values are truncated to an integer.The closest 16-bit signed integer to this @@ -859,7 +859,7 @@ exceeds the range of a 16-bit signed integer. - + Converts this object to a 32-bit signed integer. Non-integer number values are truncated to an integer. (NOTE: To determine whether this method call can succeed, call the @@ -886,7 +886,7 @@ - + Converts this object to a 64-bit signed integer. Non-integer numbers are truncated to an integer. (NOTE: To determine whether this method call can succeed, call the @@ -913,14 +913,14 @@ - + Converts this object to an 8-bit signed integer. An 8-bit signed integer. - + Converts this object to a 32-bit floating point number.The closest 32-bit floating point number to this object. @@ -930,7 +930,7 @@ type is not a number type. - + Gets the value of this object as a text string.Gets this object's string.This object's type is not a string, including if this object is @@ -945,7 +945,7 @@ - + Converts this object to a 16-bit unsigned integer. The return value will be truncated as necessary. @@ -954,7 +954,7 @@ - + Converts this object to a 32-bit unsigned integer. The return value will be truncated as necessary. @@ -963,7 +963,7 @@ - + Converts this object to a 64-bit unsigned integer. Non-integer values are truncated to an integer. @@ -973,7 +973,7 @@ - + Returns whether this object's value can be converted to a 64-bit floating point number without its value being rounded to another numerical value. @@ -984,7 +984,7 @@ - + Returns whether this object's numerical value is an integer, is -(2^31) or greater, and is less than 2^31. @@ -995,7 +995,7 @@ - + Returns whether this object's numerical value is an integer, is -(2^63) or greater, and is less than 2^63. @@ -1006,7 +1006,7 @@ - + Returns whether this object's value can be converted to a 32-bit floating point number without its value being rounded to another numerical value. @@ -1017,7 +1017,7 @@ - + Returns whether this object's value, truncated to an integer, would be -(2^31) or greater, and less than 2^31. @@ -1028,7 +1028,7 @@ - + Returns whether this object's value, truncated to an integer, would be -(2^63) or greater, and less than 2^63. @@ -1039,14 +1039,14 @@ - + Removes all items from this CBOR array or all keys and values from this CBOR map. This object is not a CBOR array or CBOR map. - + Compares two CBOR objects. In this implementation: @@ -1089,7 +1089,7 @@ null.An internal error occurred. - + Compares this object and another CBOR object, ignoring the tags they have, if any. See the CompareTo method for more information on the @@ -1101,7 +1101,7 @@ - + Determines whether a value of the given key exists in this object.An object that serves as the key.true if the given key is found, or false if the @@ -1109,7 +1109,7 @@ opposed to CBORObject.Null). - + Determines whether a value of the given key exists in this object.A string that serves as the key.true if the given key (as a CBOR object) is found, @@ -1118,20 +1118,20 @@ null. - + Gets or sets the ICBORConverter object. The ICBORConverter object. - + Gets or sets the converter's ToCBORObject method. The converter's ToCBORObject method. - + Gets the number of keys in this map, or the number of items in this array, or 0 if this item is neither an array nor a map. @@ -1140,17 +1140,17 @@ - + At the moment, use the overload of this method that takes a - object. The object + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those settings may be adopted by this overload (without a CBOREncodeOptions argument) in the next major version. Generates a CBOR object from an array of CBOR-encoded - bytes.A byte array in which a single CBOR object is encoded.A CBOR object decoded from the given byte array.There was an error in + bytes.A byte array in which a single CBOR object is encoded.A CBOR object decoded from the given byte array.There was an error in reading or parsing the data. This includes cases where not all of the byte array represents a CBOR object. This exception is also thrown if the parameter is @@ -1158,7 +1158,7 @@ is null. - + Generates a CBOR object from an array of CBOR-encoded bytes, using the given CBOREncodeOptions @@ -1172,7 +1172,7 @@ A CBOR object decoded from the given byte array. - There was an error in reading or parsing the data. This includes cases + There was an error in reading or parsing the data. This includes cases where not all of the byte array represents a CBOR object. This exception is also thrown if the parameter @@ -1197,7 +1197,7 @@ - + Divides a CBORObject object by the value of a CBORObject object. The parameter @@ -1210,10 +1210,10 @@ - + At the moment, use the overload of this method that takes a - + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those @@ -1224,14 +1224,14 @@ array of that representation. If the CBOR object contains CBOR maps, or is a CBOR map itself, the keys to the map are written out to the byte array in an undefined order. The example code given in - + can be used to write out certain keys of a CBOR map in a given order. A byte array in CBOR format. - + Writes the binary representation of this CBOR object and returns a byte array of that representation, using the specified @@ -1239,14 +1239,14 @@ contains CBOR maps, or is a CBOR map itself, the keys to the map are written out to the byte array in an undefined order. The example code given in - + can be used to write out certain keys of a CBOR map in a given order.Options for encoding the data to CBOR.A byte array in CBOR format.The parameter is null. - + Compares the equality of two CBOR objects. Not-a-number values can be considered equal by this method. @@ -1257,7 +1257,7 @@ - + Determines whether this object and another object are equal and have the same type. Not-a-number values can be considered equal by this method. @@ -1270,17 +1270,17 @@ - + Represents the value false. - + At the moment, use the overload of this method that takes a - object. The object + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those settings may be adopted by this overload (without a CBOREncodeOptions argument) in the next major @@ -1291,11 +1291,11 @@ will be used for each duplicated key.A string in JSON format. The entire string must contain a single JSON object and not multiple objects. The string may not begin with a byte-order mark (U+FEFF).A CBORObject object.The parameter - is null.The string is not in + is null.The string is not in JSON format. - + Generates a CBOR object from a text string in JavaScript Object Notation (JSON) format, using the specified options to @@ -1305,11 +1305,11 @@ contain a single JSON object and not multiple objects. The string may not begin with a byte-order mark (U+FEFF).The parameter is a CBOREncodeOptions object.A CBORObject object.The parameter - is null.The string is not in + is null.The string is not in JSON format. - + Generates a CBOR object from an arbitrary-precision integer. An arbitrary-precision value. @@ -1317,7 +1317,7 @@ - + Generates a CBOR object from a CBOR object. The parameter @@ -1327,7 +1327,7 @@ - + Generates a CBOR object from an array of CBOR objects. An array of CBOR objects. @@ -1336,7 +1336,7 @@ - + Generates a CBOR object from a decimal number. An arbitrary-precision decimal number. @@ -1344,7 +1344,7 @@ - + Generates a CBOR object from an arbitrary-precision binary floating-point number. @@ -1353,7 +1353,7 @@ - + Generates a CBOR object from an arbitrary-precision binary floating-point number. @@ -1362,7 +1362,7 @@ - + Generates a CBOR object from a decimal number. An arbitrary-precision decimal number. @@ -1370,7 +1370,7 @@ - + Generates a CBOR object from an arbitrary-precision binary floating-point number. @@ -1379,7 +1379,7 @@ - + Generates a CBOR object from an arbitrary-precision integer. An arbitrary-precision value. @@ -1387,7 +1387,7 @@ - + Generates a CBOR object from a rational number. A rational number. @@ -1395,7 +1395,7 @@ - + Returns the CBOR true value or false value, depending on "value". Either True or False. @@ -1403,7 +1403,7 @@ - + Generates a CBOR object from a byte (0 to 255). The parameter @@ -1413,7 +1413,7 @@ - + Generates a CBOR object from a byte array. The byte array is copied to a new byte array. (This method can't be used to @@ -1445,7 +1445,7 @@ - + Generates a CBOR string object from a Unicode character.The parameter is a @@ -1455,7 +1455,7 @@ to convert them to integers instead. - + Converts a .NET decimal to a CBOR object. The parameter @@ -1465,7 +1465,7 @@ - + Generates a CBOR object from a 64-bit floating-point number. The parameter @@ -1475,7 +1475,7 @@ - + Generates a CBOR object from a 16-bit signed integer. The parameter @@ -1485,7 +1485,7 @@ - + Generates a CBOR object from a 32-bit signed integer. The parameter @@ -1495,7 +1495,7 @@ - + Generates a CBOR object from an array of 32-bit integers. An array of 32-bit integers. @@ -1504,7 +1504,7 @@ - + Generates a CBOR object from a 64-bit signed integer. The parameter @@ -1514,7 +1514,7 @@ - + Generates a CBOR object from an array of 64-bit integers. An array of 64-bit integers. @@ -1523,7 +1523,7 @@ - + Generates a CBORObject from an arbitrary object. See the overload of this method that takes a PODOptions argument. @@ -1535,7 +1535,7 @@ - + Generates a CBORObject from an arbitrary object, using the given options to control how certain objects are converted to CBOR @@ -1581,7 +1581,7 @@ instead.). If the input is a text string, a CBOR text string object will be created. To create a CBOR byte string object from a text string, see the example given in -. +. REMARK: The behavior of this method is likely to change in the next major version (4.0). There are certain inconsistencies between the ToObject method and the FromObject method as well as between the .NET and Java versions of FromObject. For one thing, DateTime/Date objects are converted differently between the two versions -- either as CBOR maps with their "get" properties (Java) or as tag-0 strings (.NET) -- this difference has to remain for backward compatibility with version 3.0. For another thing, the treatment of properties/getters starting with "Is" is subtly inconsistent between the .NET and Java versions of FromObject, especially when using certain PODOptions. A certain consistency between .NET and Java and between FromObject and ToObject are sought for version 4.0. It is also hoped that-- the ToObject method will support deserializing to objects consisting of fields and not getters ("getX()" methods), both in .NET and in Java, and both FromObject and ToObject will be better designed, in version 4.0, so that backward-compatible improvements are easier to make. @@ -1591,7 +1591,7 @@ CBORObject.Null if the object is null.The parameter is null. - + Converts a signed 8-bit integer to a CBOR object. The parameter @@ -1601,7 +1601,7 @@ - + Generates a CBOR object from a 32-bit floating-point number. The parameter @@ -1611,14 +1611,14 @@ - + Generates a CBOR object from a text string.A string value. Can be null.A CBOR object representing the string, or CBORObject.Null if stringValue is null.The string contains an unpaired surrogate code point. - + Converts a 16-bit unsigned integer to a CBOR object. A 16-bit unsigned integer. @@ -1626,7 +1626,7 @@ - + Converts a 32-bit unsigned integer to a CBOR object. A 32-bit unsigned integer. @@ -1634,7 +1634,7 @@ - + Converts a 64-bit unsigned integer to a CBOR object. A 64-bit unsigned integer. @@ -1642,7 +1642,7 @@ - + Generates a CBOR object from an arbitrary object and gives the resulting object a tag.An arbitrary object. If the tag number is 2 @@ -1659,7 +1659,7 @@ is null. - + Generates a CBOR object from an arbitrary object and gives the resulting object a tag.An arbitrary object. If the tag number is 2 @@ -1676,7 +1676,7 @@ is null. - + Generates a CBOR object from an arbitrary object and gives the resulting object a tag.An arbitrary object. If the tag number @@ -1692,7 +1692,7 @@ is less than 0 or 's type is unsupported. - + Generates a CBOR object from an arbitrary object and gives the resulting object a tag. @@ -1710,7 +1710,7 @@ - + Generates a CBOR object from an enumerable set of objects. An object that implements the IEnumerable interface. In the .NET version, @@ -1724,7 +1724,7 @@ - + Generates a CBOR object from a list of objects. An array of CBOR objects. Can be null. @@ -1736,7 +1736,7 @@ - + Generates a CBOR object from a map of objects. A map of CBOR objects. @@ -1751,7 +1751,7 @@ - + Creates a CBOR object from a simple value number.The parameter is a 32-bit signed integer.A CBORObject object.The parameter @@ -1759,14 +1759,14 @@ from 24 through 31. - + Gets a list of all tags, from outermost to innermost. An array of tags, or the empty string if this object is untagged. - + Gets the byte array used in this object, if this object is a byte string, without copying the data to a new one. This method's @@ -1775,7 +1775,7 @@ not a byte string. - + Calculates the hash code of this object. No application or process IDs are used in the hash code calculation. @@ -1783,14 +1783,14 @@ - + Gets a list of all tags, from outermost to innermost. An array of tags, or the empty string if this object is untagged. - + Returns whether this object has a tag of the given number.The tag value to search for.true if this object has a tag of the given number; otherwise, false.BigTagValue is @@ -1798,7 +1798,7 @@ than 0. - + Returns whether this object has a tag of the given number.The tag value to search for.true if this object has a tag of the given number; otherwise, false.BigTagValue is @@ -1806,7 +1806,7 @@ than 0. - + Returns whether this object has a tag of the given number.The tag value to search for.true if this object has a tag of the given number; otherwise, false.TagValue is less than @@ -1814,7 +1814,7 @@ "obj" is null. - + Gets the last defined tag for this CBOR data item, or -1 if the item is untagged. @@ -1823,7 +1823,7 @@ - + Inserts an object at the specified position in this CBOR array.Zero-based index to insert at.An object representing the value, which will @@ -1833,7 +1833,7 @@ has an unsupported type; or is not a valid index into this array. - + Gets a value indicating whether this value is a CBOR false value. true @@ -1843,14 +1843,14 @@ - + Gets a value indicating whether this CBOR object represents a finite number.true If this CBOR object represents a finite number; otherwise, . false. - + Gets a value indicating whether this CBOR object represents infinity. true @@ -1859,7 +1859,7 @@ - + Gets a value indicating whether this object represents an integral number, that is, a number without a fractional part. @@ -1868,7 +1868,7 @@ false. - + Gets a value indicating whether this CBOR object represents a not-a-number value (as opposed to whether this object's type is not a @@ -1880,7 +1880,7 @@ - + Gets a value indicating whether this object is a negative number. true @@ -1890,7 +1890,7 @@ - + Gets a value indicating whether this CBOR object represents negative infinity. @@ -1900,14 +1900,14 @@ - + Gets a value indicating whether this value is a CBOR null value.true If this value is a CBOR null value; otherwise, . false. - + Gets a value indicating whether this CBOR object represents positive infinity. @@ -1917,28 +1917,28 @@ - + Gets a value indicating whether this data item has at least one tag.true If this data item has at least one tag; otherwise, . false. - + Gets a value indicating whether this value is a CBOR true value.true If this value is a CBOR true value; otherwise, . false. - + Gets a value indicating whether this value is a CBOR undefined value.true If this value is a CBOR undefined value; otherwise, . false. - + Gets a value indicating whether this object's value equals 0. true @@ -1947,7 +1947,7 @@ . - + Gets the value of a CBOR object in this map, using a CBOR object as the key.The parameter is a CBOR @@ -1957,7 +1957,7 @@ not a map. - + Gets the value of a CBOR object by integer index in this array.Zero-based index of the element.A CBORObject object.This object is @@ -1965,7 +1965,7 @@ "value" is null (as opposed to CBORObject.Null). - + Gets the value of a CBOR object in this map, using a string as the key.A key that points to the desired value.A CBORObject object.The key is @@ -1973,14 +1973,14 @@ not a map. - + Gets a collection of the keys of this CBOR object in an undefined order.A collection of the keys of this CBOR object.This object is not a map. - + Gets the last defined tag for this CBOR data item, or -1 if the item is untagged. @@ -1989,7 +1989,7 @@ - + Gets the outermost tag for this CBOR data item, or -1 if the item is untagged. @@ -1998,7 +1998,7 @@ - + Multiplies two CBOR numbers.The parameter is a CBOR object.The parameter is a @@ -2007,45 +2007,45 @@ NaN). - + A not-a-number value. - + Gets this object's value with the sign reversed.The reversed-sign form of this number.This object's type is not a number type. - + The value negative infinity. - + Creates a new empty CBOR array. A new CBOR array. - + Creates a new empty CBOR map. A new CBOR map. - + Represents the value null. - + Gets the outermost tag for this CBOR data item, or -1 if the item is untagged. @@ -2054,17 +2054,17 @@ - + The value positive infinity. - + At the moment, use the overload of this method that takes a - object. The object + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those settings may be adopted by this overload (without a CBOREncodeOptions argument) in the next major @@ -2073,22 +2073,22 @@ method will read from the stream until the end of the CBOR object is reached or an error occurs, whichever happens first.A readable data stream.A CBOR object that was read.The parameter - is null.There was an error in + is null.There was an error in reading or parsing the data. - + Reads an object in CBOR format from a data stream, using the specified options to control the decoding process. This method will read from the stream until the end of the CBOR object is reached or an error occurs, whichever happens first.A readable data stream.The parameter is a CBOREncodeOptions object.A CBOR object that was read.The parameter - is null.There was an error in + is null.There was an error in reading or parsing the data. - + Generates a CBOR object from a data stream in JavaScript Object Notation (JSON) format. The JSON stream may begin with a @@ -2102,11 +2102,11 @@ read from the data stream must contain a single JSON object and not multiple objects.A CBORObject object.The parameter is null.An I/O error - occurred.The data stream + occurred.The data stream contains invalid encoding or is not in JSON format. - + Generates a CBOR object from a data stream in JavaScript Object Notation (JSON) format, using the specified options to @@ -2122,11 +2122,11 @@ multiple objects.The parameter is a CBOREncodeOptions object.A CBORObject object.The parameter is null.An I/O error - occurred.The data stream + occurred.The data stream contains invalid encoding or is not in JSON format. - + Finds the remainder that results when a CBORObject object is divided by the value of a CBORObject object. @@ -2140,7 +2140,7 @@ - + If this object is an array, removes the first instance of the specified item from the array. If this object is a map, removes @@ -2150,7 +2150,7 @@ not an array or map. - + If this object is an array, removes the first instance of the specified item (once converted to a CBOR object) from the array. If this object is a map, removes @@ -2160,7 +2160,7 @@ not an array or map. - + Removes the item at the given index of this CBOR array. The index, starting at 0, of the item to remove. @@ -2172,7 +2172,7 @@ This object is not a CBOR array. - + Maps an object to a key in this CBOR map, or adds the value if the key doesn't exist.An object representing the key, which will be @@ -2185,7 +2185,7 @@ unsupported type. - + Gets this value's sign: -1 if negative; 1 if positive; 0 if zero. This value's sign: -1 if negative; 1 if positive; 0 if zero. @@ -2194,7 +2194,7 @@ not-a-number value (NaN). - + Gets the simple value ID of this object, or -1 if this object is not a simple value (including if the value is a floating-point number). @@ -2203,7 +2203,7 @@ - + Finds the difference between two CBOR number objects.The parameter is a @@ -2213,7 +2213,7 @@ NaN). - + Converts this object to a string in JavaScript Object Notation (JSON) format, using the specified options to control the @@ -2222,12 +2222,12 @@ If the CBOR object contains CBOR maps, or is a CBOR map itself, the keys to the map are written out to the JSON string in an undefined order. The example code given in - + can be used to write out certain keys of a CBOR map in a given order to a JSON string.A text string. - + Converts this object to a string in JavaScript Object Notation (JSON) format, using the specified options to control the encoding process. This function works not only with arrays and maps, but also integers, strings, @@ -2304,7 +2304,7 @@ is null. - + Converts this CBOR object to an object of an arbitrary type. See the documentation for the overload of this method taking a CBORTypeMapper parameter for more information. This method (without a CBORTypeMapper @@ -2358,7 +2358,7 @@ - + Converts this CBOR object to an object of an arbitrary type. See the documentation for the overload of this method taking @@ -2384,7 +2384,7 @@ object. - + Converts this CBOR object to an object of an arbitrary type. The following cases are checked in the logical order given (rather than the strict order in which they are implemented by this library): @@ -2581,7 +2581,7 @@ eligible setter with the corresponding value in the CBOR map, if any. Key names in the map are matched to eligible setters according to the rules described in the - + documentation. Note that for security reasons, certain types are not supported even if they contain eligible setters. @@ -2653,7 +2653,7 @@ - + Converts this CBOR object to an object of an arbitrary type. See the documentation for the overload of this method taking @@ -2681,9 +2681,9 @@ object. - + - Converts this CBOR object to an object of an arbitrary type. See for further information. + Converts this CBOR object to an object of an arbitrary type. See for further information. The type, class, or interface that this method's return value will belong to. Note: For security reasons, an application should not base this parameter on user input or other externally supplied @@ -2694,11 +2694,11 @@ - + Converts this CBOR object to an object of an arbitrary type. See - for + for further information.This parameter controls which data types are eligible for Plain-Old-Data deserialization and includes custom converters from CBOR objects to certain data types. @@ -2714,11 +2714,11 @@ "T", or this object's CBOR type, is not supported. - + Converts this CBOR object to an object of an arbitrary type. See - for + for further information.This parameter controls which data types are eligible for Plain-Old-Data deserialization and includes custom converters from CBOR objects to certain data types. @@ -2735,11 +2735,11 @@ "T", or this object's CBOR type, is not supported. - + Converts this CBOR object to an object of an arbitrary type. See - for + for further information.Specifies options for controlling deserialization of CBOR objects. The type, class, or interface that this method's return value will belong to. Note: For security @@ -2753,37 +2753,37 @@ "T", or this object's CBOR type, is not supported. - + Returns this CBOR object in string form. The format is intended to be human-readable, not machine-readable, the format is not intended to be parsed, and the format may change at any time. The returned string is not necessarily in JavaScript Object Notation (JSON); to convert -CBOR objects to JSON strings, use the method instead. +CBOR objects to JSON strings, use the method instead. A text representation of this object. - + Represents the value true. - + Gets the general data type of this CBOR object. The general data type of this CBOR object. - + Represents the value undefined. - + Gets an object with the same value as this one but without the tags it has, if any. If this object is an array, map, or byte string, the data @@ -2793,7 +2793,7 @@ CBOR objects to JSON strings, use the Big integer to write. Can be null.A writable data stream.The parameter @@ -2823,13 +2823,13 @@ CBOR objects to JSON strings, use the The value to write. Can be null.A writable data stream.The parameter is null. - + Writes a decimal floating-point number in CBOR format to a data stream, as follows: @@ -2849,7 +2849,7 @@ CBOR objects to JSON strings, use the - + Writes a rational number in CBOR format to a data stream.An arbitrary-precision rational @@ -2877,7 +2877,7 @@ CBOR objects to JSON strings, use the - + Writes a binary floating-point number in CBOR format to a data stream as follows: @@ -2916,7 +2916,7 @@ CBOR objects to JSON strings, use the Big integer to write. Can be null.A writable data stream.The parameter @@ -2924,7 +2924,7 @@ CBOR objects to JSON strings, use the An arbitrary-precision rational @@ -2933,7 +2933,7 @@ CBOR objects to JSON strings, use the The value to write.A writable data stream.The parameter @@ -2941,7 +2941,7 @@ CBOR objects to JSON strings, use the - + Writes a Unicode character as a string in CBOR format to a data stream.The value to write.A writable data stream.The parameter @@ -2961,7 +2961,7 @@ CBOR objects to JSON strings, use the The value to write.A writable data stream.The parameter @@ -2969,7 +2969,7 @@ CBOR objects to JSON strings, use the The value to write.A writable data stream.The parameter @@ -2977,7 +2977,7 @@ CBOR objects to JSON strings, use the The value to write.A writable data stream.The parameter @@ -2985,7 +2985,7 @@ CBOR objects to JSON strings, use the The value to write.A writable data stream.The parameter @@ -2993,10 +2993,10 @@ CBOR objects to JSON strings, use the + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those @@ -3013,7 +3013,7 @@ CBOR objects to JSON strings, use the + can be used to write out certain keys of a CBOR map in a given order. Currently, the following objects are supported: @@ -3043,7 +3043,7 @@ CBOR objects to JSON strings, use the The parameter @@ -3053,7 +3053,7 @@ CBOR objects to JSON strings, use the The value to write.A writable data stream.The parameter @@ -3061,11 +3061,11 @@ CBOR objects to JSON strings, use the object. The object + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those settings may be adopted by this overload (without a CBOREncodeOptions argument) in the next major @@ -3078,7 +3078,7 @@ CBOR objects to JSON strings, use the The string to write. Can be null.A writable data stream.Options for encoding the data to @@ -3087,7 +3087,7 @@ CBOR objects to JSON strings, use the A 16-bit unsigned integer. @@ -3095,7 +3095,7 @@ CBOR objects to JSON strings, use the A 32-bit unsigned integer. @@ -3103,7 +3103,7 @@ CBOR objects to JSON strings, use the A 64-bit unsigned integer. @@ -3114,14 +3114,14 @@ CBOR objects to JSON strings, use the + can be used to write out certain keys of a CBOR map in a given order to a JSON string. The parameter @@ -3131,21 +3131,21 @@ CBOR objects to JSON strings, use the + can be used to write out certain keys of a CBOR map in a given order to a JSON string.A writable data stream.An I/O error occurred.The parameter is null. - + Converts this object to a string in JavaScript Object Notation (JSON) format, as in the ToJSONString method, and writes @@ -3153,7 +3153,7 @@ CBOR objects to JSON strings, use the + can be used to write out certain keys of a CBOR map in a given order to a JSON string.A writable data stream.An object containing the options to control writing the CBOR object to JSON.An I/O error @@ -3161,9 +3161,9 @@ CBOR objects to JSON strings, use the is null. - + At the moment, use the overload of this method that takes a - + object. The object CBOREncodeOptions.Default contains recommended settings for CBOREncodeOptions, and those @@ -3241,14 +3241,14 @@ CBOR objects to JSON strings, use the + can be used to write out certain keys of a CBOR map in a given order.A writable data stream.Options for encoding the data to CBOR.The parameter @@ -3257,7 +3257,7 @@ CBOR objects to JSON strings, use the - + Writes a CBOR major type number and an integer 0 or greater associated with it to a data stream, where that integer is @@ -3327,7 +3327,7 @@ stream.Write(bytes, 0, bytes.Length); - + Writes a CBOR major type number and an integer 0 or greater associated with it to a data stream, where that integer is @@ -3350,7 +3350,7 @@ stream.Write(bytes, 0, bytes.Length); and major type is 7.The parameter is null. - + Writes a CBOR major type number and an integer 0 or greater associated with it to a data stream, where that integer is @@ -3372,7 +3372,7 @@ stream.Write(bytes, 0, bytes.Length); stream.The parameter is null. - + Writes a CBOR major type number and an integer 0 or greater associated with it to a data stream, where that integer is @@ -3396,13 +3396,13 @@ stream.Write(bytes, 0, bytes.Length); 255.The parameter is null. - + Gets a CBOR object for the number zero. - + Adds two CBOR objects and returns their result. The parameter @@ -3415,7 +3415,7 @@ stream.Write(bytes, 0, bytes.Length); - + Divides a CBORObject object by the value of a CBORObject object. The parameter @@ -3428,7 +3428,7 @@ stream.Write(bytes, 0, bytes.Length); - + Finds the remainder that results when a CBORObject object is divided by the value of a CBORObject object. @@ -3442,7 +3442,7 @@ stream.Write(bytes, 0, bytes.Length); - + Multiplies a CBORObject object by the value of a CBORObject object. The parameter @@ -3455,7 +3455,7 @@ stream.Write(bytes, 0, bytes.Length); - + Subtracts a CBORObject object from a CBORObject object. The parameter @@ -3468,19 +3468,19 @@ stream.Write(bytes, 0, bytes.Length); - + Implements arithmetic operations with CBOR objects. - + Implements CBOR tag 3. - + Converts a UUID to a CBOR object. A UUID. @@ -3488,43 +3488,43 @@ stream.Write(bytes, 0, bytes.Length); - + A generic CBOR tag class for strings. - + Represents a type that a CBOR object can have. - + An array of CBOR objects. - + The simple values true and false. - + An array of bytes. - + A map of CBOR objects. - + A number of any kind, including integers, big integers, floating point numbers, and decimal numbers. The floating-point value Not-a-Number is @@ -3532,19 +3532,19 @@ stream.Write(bytes, 0, bytes.Length); - + A "simple value" other than floating point values, true, and false. - + A text string. - + Specifies what kinds of CBOR objects a tag can be. This class is used when a CBOR object is being read from a data stream. This class can't be @@ -3553,13 +3553,13 @@ stream.Write(bytes, 0, bytes.Length); - + A filter that allows any CBOR object. - + Determines whether this type filter allows CBOR arrays and the given array index is allowed under this type filter. @@ -3571,7 +3571,7 @@ stream.Write(bytes, 0, bytes.Length); - + Returns whether an array's length is allowed under a filter. An arbitrary-precision integer. @@ -3585,7 +3585,7 @@ stream.Write(bytes, 0, bytes.Length); - + Returns whether an array's length is allowed under this filter. The length of a CBOR array. @@ -3596,7 +3596,7 @@ stream.Write(bytes, 0, bytes.Length); - + Returns whether an array's length is allowed under a filter. The length of a CBOR array. @@ -3607,13 +3607,13 @@ stream.Write(bytes, 0, bytes.Length); - + A filter that allows byte strings. - + Gets the type filter for this array filter by its index. A zero-based index of the filter to retrieve. @@ -3623,7 +3623,7 @@ stream.Write(bytes, 0, bytes.Length); - + Gets the type filter for this array filter by its index. A zero-based index of the filter to retrieve. @@ -3633,7 +3633,7 @@ stream.Write(bytes, 0, bytes.Length); - + Returns whether the given CBOR major type matches a major type allowed by this filter. @@ -3644,13 +3644,13 @@ stream.Write(bytes, 0, bytes.Length); - + A filter that allows negative integers. - + Returns whether this filter allows simple values that are not floating-point numbers. @@ -3660,13 +3660,13 @@ stream.Write(bytes, 0, bytes.Length); - + A filter that allows no CBOR types. - + Gets a value indicating whether CBOR objects can have the given tag number. @@ -3680,7 +3680,7 @@ stream.Write(bytes, 0, bytes.Length); - + Gets a value indicating whether CBOR objects can have the given tag number. @@ -3691,7 +3691,7 @@ stream.Write(bytes, 0, bytes.Length); - + Gets a value indicating whether CBOR objects can have the given tag number. @@ -3702,26 +3702,26 @@ stream.Write(bytes, 0, bytes.Length); - + A filter that allows text strings. - + A filter that allows unsigned integers. - + Copies this filter and includes arrays of any length in the new filter. A CBORTypeFilter object. - + Copies this filter and includes CBOR arrays with an exact length to the new filter. @@ -3736,7 +3736,7 @@ stream.Write(bytes, 0, bytes.Length); - + Copies this filter and includes CBOR arrays with at least a given length to the new filter. @@ -3751,14 +3751,14 @@ stream.Write(bytes, 0, bytes.Length); - + Copies this filter and includes byte strings in the new filter. A CBORTypeFilter object. - + Copies this filter and includes floating-point numbers in the new filter. @@ -3766,21 +3766,21 @@ stream.Write(bytes, 0, bytes.Length); - + Copies this filter and includes maps in the new filter. A CBORTypeFilter object. - + Copies this filter and includes negative integers in the new filter. A CBORTypeFilter object. - + Copies this filter and includes a set of valid CBOR tags in the new filter. @@ -3789,21 +3789,21 @@ stream.Write(bytes, 0, bytes.Length); - + Copies this filter and includes text strings in the new filter. A CBORTypeFilter object. - + Copies this filter and includes unsigned integers in the new filter. A CBORTypeFilter object. - + Holds converters to customize the serialization and deserialization behavior of CBORObject.FromObject and @@ -3811,7 +3811,7 @@ stream.Write(bytes, 0, bytes.Length); ToObject - + Registers an object that converts objects of a given type to CBOR objects (called a CBOR converter).A Type object specifying the type that the @@ -3825,33 +3825,33 @@ stream.Write(bytes, 0, bytes.Length); null."Converter doesn't contain a proper ToCBORObject method". - + Adds the fully qualified name of a Java or .NET type for use in type matching.The fully qualified name of a Java or .NET class (e.g., java.math.BigInteger or System.Globalization.CultureInfo).This object.The parameter is null.The parameter is empty. - + Adds a prefix of a Java or .NET type for use in type matching. A type matches a prefix if its fully qualified name is or begins with that prefix, using codepoint-by-codepoint (case-sensitive) matching.The prefix of a Java or .NET type (e.g., `java.math.` or `System.Globalization`).This object.The parameter is null.The parameter is empty. - + Gets an internal API value. An internal API value. - + Returns whether the given Java or .NET type name fits the filters given in this mapper.The fully qualified name of a Java or .NET class (e.g., java.math.BigInteger or System.Globalization.CultureInfo).Either true if the given Java or .NET type name fits the filters given in this mapper, or false otherwise. - + Contains utility methods that may have use outside of the CBORObject class. - + Sets this object's value to the current value times another integer. The integer to multiply by. @@ -3859,14 +3859,14 @@ stream.Write(bytes, 0, bytes.Length); - + Gets the sign of this object's value. 1 if positive, -1 if negative, 0 if zero. - + Sets this object's value to the current value minus the given FastInteger value. @@ -3875,7 +3875,7 @@ stream.Write(bytes, 0, bytes.Length); - + Sets this object's value to the current value minus the given integer. The subtrahend. @@ -3883,7 +3883,7 @@ stream.Write(bytes, 0, bytes.Length); - + Interface implemented by classes that convert objects of arbitrary types to CBOR objects. @@ -3892,7 +3892,7 @@ stream.Write(bytes, 0, bytes.Length); - + Converts an object to a CBOR object. An object to convert to a CBOR object. @@ -3900,7 +3900,7 @@ stream.Write(bytes, 0, bytes.Length); - + Interface implemented by classes that convert objects of arbitrary types to and from CBOR objects. @@ -3909,7 +3909,7 @@ stream.Write(bytes, 0, bytes.Length); from CBOR objects. - + Converts a CBOR object to an object of a type supported by the implementing class. @@ -3917,18 +3917,18 @@ stream.Write(bytes, 0, bytes.Length); The converted object. - An error occurred in the conversion; for example, the conversion doesn't + An error occurred in the conversion; for example, the conversion doesn't support the given CBOR object. - + Implemented by classes that validate CBOR objects belonging to a specific tag. - + Gets a type filter specifying what kinds of CBOR objects are supported by this tag. @@ -3936,7 +3936,7 @@ stream.Write(bytes, 0, bytes.Length); - + Generates a CBOR object based on the data of another object. If the data is not valid, should throw a CBORException. @@ -3946,13 +3946,13 @@ stream.Write(bytes, 0, bytes.Length); - + An interface for reading Unicode characters from a data source. - + Reads a sequence of Unicode code points from a data source. Output buffer. @@ -3963,7 +3963,7 @@ stream.Write(bytes, 0, bytes.Length); - + Reads a Unicode character from a data source. Either a Unicode code point (from 0-0xd7ff or from 0xe000 to 0x10ffff), @@ -3971,52 +3971,52 @@ stream.Write(bytes, 0, bytes.Length); - + - Includes options to control how CBOR objects are converted + Includes options to control how CBOR objects are converted to JSON. - + - Initializes a new instance of the class with default options. + Initializes a new instance of the class with default options. - + - Initializes a new instance of the class with the given values for the options. + Initializes a new instance of the class with the given values for the options. Whether padding is included when writing data in base64url or traditional base64 format to JSON. - + Gets a value indicating whether padding is written out when writing base64url or traditional base64 to JSON.The default is false, no padding.The padding character is '='. - + The default options for converting CBOR objects to JSON. - + Options for converting "plain old data" objects to CBOR objects. - + - Initializes a new instance of the class. + Initializes a new instance of the class. - + - Initializes a new instance of the class. + Initializes a new instance of the class. If set to true remove is prefix. NOTE: May be ignored in future versions of this library. @@ -4026,34 +4026,34 @@ stream.Write(bytes, 0, bytes.Length); - + The default settings for "plain old data" options. - + Gets a value indicating whether the "Is" prefix in property names is removed before they are used as keys.true If the prefix is removed; otherwise, . false. - + Gets a value indicating whether property names are converted to camel case before they are used as keys.true If the names are converted to camel case; otherwise, . false. - + Implements CBOR string references, described at http://cbor.schmorp.de/stringref - + Contains utility methods for processing Uniform Resource Identifiers (URIs) and Internationalized Resource Identifiers (IRIs) under RFC3986 and @@ -4081,7 +4081,7 @@ vnd.shootproof+json - + Encodes characters other than "unreserved" characters for URIs. A string to encode. @@ -4093,14 +4093,14 @@ vnd.shootproof+json is null. - + Specifies whether certain characters are allowed when parsing IRIs and URIs. - + The rules only check for the appropriate delimiters when splitting the path, without checking if all the characters in each component are valid. @@ -4109,7 +4109,7 @@ vnd.shootproof+json - + The rules follow the syntax for parsing IRIs. In particular, many code points outside the Basic Latin range (U+0000 to U+007F) are allowed. @@ -4117,7 +4117,7 @@ vnd.shootproof+json - + The rules only check for the appropriate delimiters when splitting the path, without checking if all the characters in each component are valid. @@ -4127,7 +4127,7 @@ vnd.shootproof+json - + The rules only check for the appropriate delimiters when splitting the path, without checking if all the characters in each component are valid. @@ -4136,14 +4136,14 @@ vnd.shootproof+json - + The rules follow the syntax for parsing IRIs, except that code points outside the Basic Latin range (U+0000 to U+007F) are not allowed. - + Decodes percent-encoding (of the form "%XX" where X is a hexadecimal digit) in the given string. Successive percent-encoded bytes are assumed to form characters in UTF-8. @@ -4153,7 +4153,7 @@ vnd.shootproof+json The string in which percent-encoding was decoded. - + Decodes percent-encoding (of the form "%XX" where X is a hexadecimal digit) in the given portion of a string. Successive percent-encoded bytes are assumed to form characters in UTF-8. @@ -4174,7 +4174,7 @@ vnd.shootproof+json is ull. - + Escapes characters that can't appear in URIs or IRIs. The function is idempotent; that is, calling the function again on the result with the @@ -4187,7 +4187,7 @@ vnd.shootproof+json - + Determines whether the string is a valid IRI with a scheme component. This can be used to check for relative IRI references. @@ -4205,7 +4205,7 @@ vnd.shootproof+json - + Determines whether the string is a valid URI with a scheme component. This can be used to check for relative URI references. The following cases @@ -4222,7 +4222,7 @@ vnd.shootproof+json - + Determines whether the substring is a valid CURIE reference under RDFA 1.1. (The CURIE reference is the part after @@ -4235,7 +4235,7 @@ vnd.shootproof+json .The parameter is null. - + Resolves a URI or IRI relative to another URI or IRI. A string representing a URI or IRI reference. Example: @@ -4254,7 +4254,7 @@ vnd.shootproof+json - + Resolves a URI or IRI relative to another URI or IRI. A string representing a URI or IRI reference. Example: @@ -4273,7 +4273,7 @@ vnd.shootproof+json - + Parses an Internationalized Resource Identifier (IRI) reference under RFC3987. If the IRI reference is syntactically valid, splits the string @@ -4291,7 +4291,7 @@ vnd.shootproof+json - + Parses an Internationalized Resource Identifier (IRI) reference under RFC3987. If the IRI is syntactically valid, splits the string into its @@ -4312,7 +4312,7 @@ vnd.shootproof+json - + Parses a substring that represents an Internationalized Resource Identifier (IRI) under RFC3987. If the IRI is diff --git a/fido2-net-lib.sln b/fido2-net-lib.sln index f770744f..b68d3dea 100644 --- a/fido2-net-lib.sln +++ b/fido2-net-lib.sln @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaos.NaCl", "Chaos.NaCl\Chaos.NaCl.csproj", "{AE28FD14-7985-4707-A963-C94B8597AE50}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CBOR", "CBOR\CBOR.csproj", "{913DE613-DE3A-402D-9EF6-722ED255BF6A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,6 +40,10 @@ Global {AE28FD14-7985-4707-A963-C94B8597AE50}.Debug|Any CPU.Build.0 = Debug|Any CPU {AE28FD14-7985-4707-A963-C94B8597AE50}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE28FD14-7985-4707-A963-C94B8597AE50}.Release|Any CPU.Build.0 = Release|Any CPU + {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/fido2-net-lib/AuthDataHelper.cs b/fido2-net-lib/AuthDataHelper.cs index 04ce1cb4..e3926997 100644 --- a/fido2-net-lib/AuthDataHelper.cs +++ b/fido2-net-lib/AuthDataHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using PeterO.Cbor; namespace Fido2NetLib { @@ -68,9 +69,9 @@ public static HashAlgorithm GetHasher(HashAlgorithmName hashName) { 2, TpmEccCurve.TPM_ECC_NIST_P384}, { 3, TpmEccCurve.TPM_ECC_NIST_P521} }; - public static PeterO.Cbor.CBORObject CoseKeyFromCertAndAlg(X509Certificate2 cert, Int32 alg) + public static CBORObject CoseKeyFromCertAndAlg(X509Certificate2 cert, Int32 alg) { - var coseKey = PeterO.Cbor.CBORObject.NewMap(); + var coseKey = CBORObject.NewMap(); var kty = AuthDataHelper.CoseKeyTypeFromOid[cert.GetKeyAlgorithm()]; coseKey.Add(1, kty); coseKey.Add(3, alg); @@ -92,13 +93,13 @@ public static PeterO.Cbor.CBORObject CoseKeyFromCertAndAlg(X509Certificate2 cert } return coseKey; } - public static bool VerifySigWithCoseKey(byte[] data, PeterO.Cbor.CBORObject coseKey, byte[] sig) + public static bool VerifySigWithCoseKey(byte[] data, CBORObject coseKey, byte[] sig) { // Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData - var kty = coseKey[PeterO.Cbor.CBORObject.FromObject(1)].AsInt32(); - var alg = coseKey[PeterO.Cbor.CBORObject.FromObject(3)].AsInt32(); + var kty = coseKey[CBORObject.FromObject(1)].AsInt32(); + var alg = coseKey[CBORObject.FromObject(3)].AsInt32(); var crv = 0; - if (1 == kty || 2 == kty) crv = coseKey[PeterO.Cbor.CBORObject.FromObject(-1)].AsInt32(); + if (1 == kty || 2 == kty) crv = coseKey[CBORObject.FromObject(-1)].AsInt32(); switch (kty) // https://www.iana.org/assignments/cose/cose.xhtml#key-type { case 1: // OKP @@ -109,7 +110,7 @@ public static bool VerifySigWithCoseKey(byte[] data, PeterO.Cbor.CBORObject cose switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves { case 6: - return Chaos.NaCl.Ed25519.Verify(sig, GetHasher(HashAlgorithmName.SHA512).ComputeHash(data), coseKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString()); + return Chaos.NaCl.Ed25519.Verify(sig, GetHasher(HashAlgorithmName.SHA512).ComputeHash(data), coseKey[CBORObject.FromObject(-2)].GetByteString()); default: throw new ArgumentOutOfRangeException("crv"); } @@ -121,8 +122,8 @@ public static bool VerifySigWithCoseKey(byte[] data, PeterO.Cbor.CBORObject cose { var point = new ECPoint { - X = coseKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString(), - Y = coseKey[PeterO.Cbor.CBORObject.FromObject(-3)].GetByteString() + X = coseKey[CBORObject.FromObject(-2)].GetByteString(), + Y = coseKey[CBORObject.FromObject(-3)].GetByteString() }; ECCurve curve; switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms @@ -177,8 +178,8 @@ public static bool VerifySigWithCoseKey(byte[] data, PeterO.Cbor.CBORObject cose rsa.ImportParameters( new RSAParameters() { - Modulus = coseKey[PeterO.Cbor.CBORObject.FromObject(-1)].GetByteString(), - Exponent = coseKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString() + Modulus = coseKey[CBORObject.FromObject(-1)].GetByteString(), + Exponent = coseKey[CBORObject.FromObject(-2)].GetByteString() } ); RSASignaturePadding padding; @@ -302,10 +303,10 @@ public static bool IsValidPackedAttnCertSubject(string attnCertSubj) "Authenticator Attestation" == dictSubject["OU"].ToString()); } - public static Memory U2FKeyFromCOSEKey(PeterO.Cbor.CBORObject COSEKey) + public static Memory U2FKeyFromCOSEKey(CBORObject COSEKey) { - var x = COSEKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString(); - var y = COSEKey[PeterO.Cbor.CBORObject.FromObject(-3)].GetByteString(); + var x = COSEKey[CBORObject.FromObject(-2)].GetByteString(); + var y = COSEKey[CBORObject.FromObject(-3)].GetByteString(); var publicKeyU2F = new byte[1] { 0x4 }; // uncompressed return publicKeyU2F.Concat(x).Concat(y).ToArray(); } @@ -621,11 +622,11 @@ public AttestedCredentialData(byte[] attData, ref int offset) CredentialID = AuthDataHelper.GetSizedByteArray(attData, ref offset); // Determining attested credential data's length, which is variable, involves determining credentialPublicKey’s beginning location given the preceding credentialId’s length, and then determining the credentialPublicKey’s length var ms = new System.IO.MemoryStream(attData, offset, attData.Length - offset); - // PeterO.Cbor.CBORObject.Read: This method will read from the stream until the end of the CBOR object is reached or an error occurs, whichever happens first. - PeterO.Cbor.CBORObject tmp = null; + // CBORObject.Read: This method will read from the stream until the end of the CBOR object is reached or an error occurs, whichever happens first. + CBORObject tmp = null; try { - tmp = PeterO.Cbor.CBORObject.Read(ms); + tmp = CBORObject.Read(ms); } catch (Exception) { diff --git a/fido2-net-lib/AuthenticatorAttestationResponse.cs b/fido2-net-lib/AuthenticatorAttestationResponse.cs index e2561d68..12efb7d9 100644 --- a/fido2-net-lib/AuthenticatorAttestationResponse.cs +++ b/fido2-net-lib/AuthenticatorAttestationResponse.cs @@ -9,6 +9,7 @@ using Fido2NetLib.Objects; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Linq; +using PeterO.Cbor; namespace Fido2NetLib { @@ -30,22 +31,22 @@ public static AuthenticatorAttestationResponse Parse(AuthenticatorAttestationRaw if (null == rawResponse || null == rawResponse.Response) throw new Fido2VerificationException("Expected rawResponse, got null"); if (null == rawResponse.Response.AttestationObject || 0 == rawResponse.Response.AttestationObject.Length) throw new Fido2VerificationException("Missing AttestationObject"); - PeterO.Cbor.CBORObject cborAttestation = null; + CBORObject cborAttestation = null; try { - cborAttestation = PeterO.Cbor.CBORObject.DecodeFromBytes(rawResponse.Response.AttestationObject); + cborAttestation = CBORObject.DecodeFromBytes(rawResponse.Response.AttestationObject); } - catch (PeterO.Cbor.CBORException) + catch (CBORException) { throw new Fido2VerificationException("Malformed AttestationObject"); } if ( null == cborAttestation["fmt"] || - PeterO.Cbor.CBORType.TextString != cborAttestation["fmt"].Type || + CBORType.TextString != cborAttestation["fmt"].Type || null == cborAttestation["attStmt"] || - PeterO.Cbor.CBORType.Map != cborAttestation["attStmt"].Type || + CBORType.Map != cborAttestation["attStmt"].Type || null == cborAttestation["authData"] || - PeterO.Cbor.CBORType.ByteString != cborAttestation["authData"].Type + CBORType.ByteString != cborAttestation["authData"].Type ) throw new Fido2VerificationException("Malformed AttestationObject"); AuthenticatorAttestationResponse response = new AuthenticatorAttestationResponse(rawResponse.Response.ClientDataJson) @@ -121,16 +122,16 @@ public async Task VerifyAsync(CredentialCreateOp if (false == authData.AttestedCredentialDataPresent) throw new Fido2VerificationException("Attestation flag not set on attestation data"); var credentialId = authData.AttData.CredentialID; var credentialPublicKeyBytes = authData.AttData.CredentialPublicKey.ToArray(); - PeterO.Cbor.CBORObject credentialPublicKey = null; + CBORObject credentialPublicKey = null; var coseKty = 0; var coseAlg = 0; try { - credentialPublicKey = PeterO.Cbor.CBORObject.DecodeFromBytes(authData.AttData.CredentialPublicKey); - coseKty = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(1)].AsInt32(); - coseAlg = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(3)].AsInt32(); + credentialPublicKey = CBORObject.DecodeFromBytes(authData.AttData.CredentialPublicKey); + coseKty = credentialPublicKey[CBORObject.FromObject(1)].AsInt32(); + coseAlg = credentialPublicKey[CBORObject.FromObject(3)].AsInt32(); } - catch (PeterO.Cbor.CBORException) + catch (CBORException) { throw new Fido2VerificationException("Malformed credentialPublicKey"); } @@ -159,29 +160,29 @@ public async Task VerifyAsync(CredentialCreateOp { // https://www.w3.org/TR/webauthn/#tpm-attestation - if (null == sig || PeterO.Cbor.CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid TPM attestation signature"); + if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid TPM attestation signature"); if ("2.0" != AttestationObject.AttStmt["ver"].AsString()) throw new Fido2VerificationException("FIDO2 only supports TPM 2.0"); // Verify that the public key specified by the parameters and unique fields of pubArea is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData PubArea pubArea = null; - if (null != AttestationObject.AttStmt["pubArea"] && PeterO.Cbor.CBORType.ByteString == AttestationObject.AttStmt["pubArea"].Type && 0 != AttestationObject.AttStmt["pubArea"].GetByteString().Length) + if (null != AttestationObject.AttStmt["pubArea"] && CBORType.ByteString == AttestationObject.AttStmt["pubArea"].Type && 0 != AttestationObject.AttStmt["pubArea"].GetByteString().Length) { pubArea = new PubArea(AttestationObject.AttStmt["pubArea"].GetByteString()); } if (null == pubArea || null == pubArea.Unique || 0 == pubArea.Unique.Length) throw new Fido2VerificationException("Missing or malformed pubArea"); if (3 == coseKty) // RSA { - var coseMod = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-1)].GetByteString(); // modulus - var coseExp = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString(); // exponent + var coseMod = credentialPublicKey[CBORObject.FromObject(-1)].GetByteString(); // modulus + var coseExp = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); // exponent if (!coseMod.ToArray().SequenceEqual(pubArea.Unique.ToArray())) throw new Fido2VerificationException("Public key mismatch between pubArea and credentialPublicKey"); if ((coseExp[0] + (coseExp[1] << 8) + (coseExp[2] << 16)) != pubArea.Exponent) throw new Fido2VerificationException("Public key exponent mismatch between pubArea and credentialPublicKey"); } else if (2 == coseKty) // ECC { - var curve = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-1)].AsInt32(); - var X = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString(); - var Y = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-3)].GetByteString(); + var curve = credentialPublicKey[CBORObject.FromObject(-1)].AsInt32(); + var X = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); + var Y = credentialPublicKey[CBORObject.FromObject(-3)].GetByteString(); if (pubArea.EccCurve != AuthDataHelper.CoseCurveToTpm[curve]) throw new Fido2VerificationException("Curve mismatch between pubArea and credentialPublicKey"); if (!pubArea.ECPoint.X.SequenceEqual(X)) throw new Fido2VerificationException("X-coordinate mismatch between pubArea and credentialPublicKey"); @@ -192,7 +193,7 @@ public async Task VerifyAsync(CredentialCreateOp // Validate that certInfo is valid CertInfo certInfo = null; - if (null != AttestationObject.AttStmt["certInfo"] && PeterO.Cbor.CBORType.ByteString == AttestationObject.AttStmt["certInfo"].Type && 0 != AttestationObject.AttStmt["certInfo"].GetByteString().Length) + if (null != AttestationObject.AttStmt["certInfo"] && CBORType.ByteString == AttestationObject.AttStmt["certInfo"].Type && 0 != AttestationObject.AttStmt["certInfo"].GetByteString().Length) { certInfo = new CertInfo(AttestationObject.AttStmt["certInfo"].GetByteString()); } @@ -201,21 +202,21 @@ public async Task VerifyAsync(CredentialCreateOp // handled in parser, see certInfo.Magic // Verify that extraData is set to the hash of attToBeSigned using the hash algorithm employed in "alg" - if (null == alg || PeterO.Cbor.CBORType.Number != alg.Type || false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid TPM attestation algorithm"); + if (null == alg || CBORType.Number != alg.Type || false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid TPM attestation algorithm"); if (!AuthDataHelper.GetHasher(AuthDataHelper.algMap[alg.AsInt32()]).ComputeHash(data).SequenceEqual(certInfo.ExtraData)) throw new Fido2VerificationException("Hash value mismatch extraData and attToBeSigned"); // Verify that attested contains a TPMS_CERTIFY_INFO structure, whose name field contains a valid Name for pubArea, as computed using the algorithm in the nameAlg field of pubArea if (false == AuthDataHelper.GetHasher(AuthDataHelper.algMap[(int)certInfo.Alg]).ComputeHash(pubArea.Raw).SequenceEqual(certInfo.AttestedName)) throw new Fido2VerificationException("Hash value mismatch attested and pubArea"); // If x5c is present, this indicates that the attestation type is not ECDAA - if (null != x5c && PeterO.Cbor.CBORType.Array == x5c.Type && 0 != x5c.Count) + if (null != x5c && CBORType.Array == x5c.Type && 0 != x5c.Count) { - if (null == x5c.Values || 0 == x5c.Values.Count || PeterO.Cbor.CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in TPM attestation"); + if (null == x5c.Values || 0 == x5c.Values.Count || CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in TPM attestation"); // Verify the sig is a valid signature over certInfo using the attestation public key in aikCert with the algorithm specified in alg. var aikCert = new X509Certificate2(x5c.Values.First().GetByteString()); - PeterO.Cbor.CBORObject coseKey = AuthDataHelper.CoseKeyFromCertAndAlg(aikCert, alg.AsInt32()); + CBORObject coseKey = AuthDataHelper.CoseKeyFromCertAndAlg(aikCert, alg.AsInt32()); if (true != AuthDataHelper.VerifySigWithCoseKey(certInfo.Raw, coseKey, sig.GetByteString())) throw new Fido2VerificationException("Bad signature in TPM with aikCert"); @@ -273,11 +274,11 @@ public async Task VerifyAsync(CredentialCreateOp // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields if (0 == AttestationObject.AttStmt.Keys.Count || 0 == AttestationObject.AttStmt.Values.Count) throw new Fido2VerificationException("Attestation format packed must have attestation statement"); - if (null == sig || PeterO.Cbor.CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid packed attestation signature"); + if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid packed attestation signature"); // 2a. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash // using the attestation public key in attestnCert with the algorithm specified in alg - if (null == x5c && PeterO.Cbor.CBORType.Array != x5c.Type && 0 == x5c.Count) throw new Fido2VerificationException("Malformed x5c in android-key attestation"); - if (null == x5c.Values || 0 == x5c.Values.Count || PeterO.Cbor.CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in android-key attestation"); + if (null == x5c && CBORType.Array != x5c.Type && 0 == x5c.Count) throw new Fido2VerificationException("Malformed x5c in android-key attestation"); + if (null == x5c.Values || 0 == x5c.Values.Count || CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in android-key attestation"); X509Certificate2 androidKeyCert = null; ECDsaCng androidKeyPubKey = null; try @@ -289,15 +290,15 @@ public async Task VerifyAsync(CredentialCreateOp { throw new Fido2VerificationException("Failed to extract public key from android key" + ex.Message); } - if (null == alg || PeterO.Cbor.CBORType.Number != alg.Type || false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid attestation algorithm"); + if (null == alg || CBORType.Number != alg.Type || false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid attestation algorithm"); if (true != androidKeyPubKey.VerifyData(data, AuthDataHelper.SigFromEcDsaSig(sig.GetByteString()), AuthDataHelper.algMap[alg.AsInt32()])) throw new Fido2VerificationException("Invalid android key signature"); var cng = ECDsaCng.Create(new ECParameters { Curve = ECCurve.NamedCurves.nistP256, Q = new ECPoint { - X = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-2)].GetByteString(), - Y = credentialPublicKey[PeterO.Cbor.CBORObject.FromObject(-3)].GetByteString() + X = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(), + Y = credentialPublicKey[CBORObject.FromObject(-3)].GetByteString() } }); // Verify that the public key in the first certificate in in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData. @@ -331,12 +332,12 @@ public async Task VerifyAsync(CredentialCreateOp // https://www.w3.org/TR/webauthn/#android-safetynet-attestation // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields - if ((PeterO.Cbor.CBORType.TextString != AttestationObject.AttStmt["ver"].Type) || (0 == AttestationObject.AttStmt["ver"].AsString().Length)) throw new Fido2VerificationException("Invalid version in SafetyNet data"); + if ((CBORType.TextString != AttestationObject.AttStmt["ver"].Type) || (0 == AttestationObject.AttStmt["ver"].AsString().Length)) throw new Fido2VerificationException("Invalid version in SafetyNet data"); // Verify that response is a valid SafetyNet response of version ver var ver = AttestationObject.AttStmt["ver"].AsString(); - if ((PeterO.Cbor.CBORType.ByteString != AttestationObject.AttStmt["response"].Type) || (0 == AttestationObject.AttStmt["response"].GetByteString().Length)) throw new Fido2VerificationException("Invalid response in SafetyNet data"); + if ((CBORType.ByteString != AttestationObject.AttStmt["response"].Type) || (0 == AttestationObject.AttStmt["response"].GetByteString().Length)) throw new Fido2VerificationException("Invalid response in SafetyNet data"); var response = AttestationObject.AttStmt["response"].GetByteString(); var signedAttestationStatement = Encoding.UTF8.GetString(response); var jwtToken = new JwtSecurityToken(signedAttestationStatement); @@ -407,10 +408,10 @@ public async Task VerifyAsync(CredentialCreateOp if (false == authData.AttData.Aaguid.SequenceEqual(Guid.Empty.ToByteArray())) throw new Fido2VerificationException("Aaguid was not empty parsing fido-u2f atttestation statement"); // 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. - if (null == x5c || PeterO.Cbor.CBORType.Array != x5c.Type || x5c.Count != 1) throw new Fido2VerificationException("Malformed x5c in fido - u2f attestation"); + if (null == x5c || CBORType.Array != x5c.Type || x5c.Count != 1) throw new Fido2VerificationException("Malformed x5c in fido - u2f attestation"); // 2a. the attestation certificate attestnCert MUST be the first element in the array - if (null == x5c.Values || 0 == x5c.Values.Count || PeterO.Cbor.CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in fido-u2f attestation"); + if (null == x5c.Values || 0 == x5c.Values.Count || CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in fido-u2f attestation"); var cert = new X509Certificate2(x5c.Values.First().GetByteString()); // 2b. If certificate public key is not an Elliptic Curve (EC) public key over the P-256 curve, terminate this algorithm and return an appropriate error @@ -428,7 +429,7 @@ public async Task VerifyAsync(CredentialCreateOp verificationData = verificationData.Concat(hashedRpId).Concat(hashedClientDataJson).Concat(credentialId).Concat(publicKeyU2F.ToArray()).ToArray(); // 6. Verify the sig using verificationData and certificate public key - if (null == sig || PeterO.Cbor.CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); + if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); var ecsig = AuthDataHelper.SigFromEcDsaSig(sig.GetByteString()); if (null == ecsig) throw new Fido2VerificationException("Failed to decode fido-u2f attestation signature from ASN.1 encoded form"); if (true != pubKey.VerifyData(verificationData, ecsig, AuthDataHelper.algMap[coseAlg])) throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); @@ -445,17 +446,17 @@ public async Task VerifyAsync(CredentialCreateOp // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. if (0 == AttestationObject.AttStmt.Keys.Count || 0 == AttestationObject.AttStmt.Values.Count) throw new Fido2VerificationException("Attestation format packed must have attestation statement"); - if (null == sig || PeterO.Cbor.CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid packed attestation signature"); - if (null == alg || PeterO.Cbor.CBORType.Number != alg.Type) throw new Fido2VerificationException("Invalid packed attestation algorithm"); + if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid packed attestation signature"); + if (null == alg || CBORType.Number != alg.Type) throw new Fido2VerificationException("Invalid packed attestation algorithm"); // If x5c is present, this indicates that the attestation type is not ECDAA if (null != x5c) { - if (PeterO.Cbor.CBORType.Array != x5c.Type || 0 == x5c.Count || null != ecdaaKeyId) throw new Fido2VerificationException("Malformed x5c array in packed attestation statement"); - IEnumerator enumerator = x5c.Values.GetEnumerator(); + if (CBORType.Array != x5c.Type || 0 == x5c.Count || null != ecdaaKeyId) throw new Fido2VerificationException("Malformed x5c array in packed attestation statement"); + IEnumerator enumerator = x5c.Values.GetEnumerator(); while (enumerator.MoveNext()) { - if (null == enumerator || null == enumerator.Current || PeterO.Cbor.CBORType.ByteString != enumerator.Current.Type || 0 == enumerator.Current.GetByteString().Length) throw new Fido2VerificationException("Malformed x5c cert found in packed attestation statement"); + if (null == enumerator || null == enumerator.Current || CBORType.ByteString != enumerator.Current.Type || 0 == enumerator.Current.GetByteString().Length) throw new Fido2VerificationException("Malformed x5c cert found in packed attestation statement"); var x5ccert = new X509Certificate2(enumerator.Current.GetByteString()); if (DateTime.UtcNow < x5ccert.NotBefore || DateTime.UtcNow > x5ccert.NotAfter) throw new Fido2VerificationException("Packed signing certificate expired or not yet valid"); } diff --git a/fido2-net-lib/Fido2NetLib.csproj b/fido2-net-lib/Fido2NetLib.csproj index fbfa056f..fb06756f 100644 --- a/fido2-net-lib/Fido2NetLib.csproj +++ b/fido2-net-lib/Fido2NetLib.csproj @@ -20,7 +20,6 @@ - @@ -28,6 +27,7 @@ + From deefd86537bca5376a19408737c0311c14277d9d Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Fri, 19 Oct 2018 17:54:55 -0400 Subject: [PATCH 05/10] Add Yubico root and aaguid to metadata --- .../AuthenticatorAttestationResponse.cs | 39 +++++++------- fido2-net-lib/MetadataService.cs | 51 +++++++++++++++---- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/fido2-net-lib/AuthenticatorAttestationResponse.cs b/fido2-net-lib/AuthenticatorAttestationResponse.cs index 12efb7d9..7c3e0055 100644 --- a/fido2-net-lib/AuthenticatorAttestationResponse.cs +++ b/fido2-net-lib/AuthenticatorAttestationResponse.cs @@ -545,35 +545,36 @@ public async Task VerifyAsync(CredentialCreateOp { MetadataTOCPayloadEntry entry = metadataService.GetEntry(authData.AttData.GuidAaguid); - if (null != entry) + if (null != entry && null != entry.MetadataStatement) { if (entry.Hash != entry.MetadataStatement.Hash) throw new Fido2VerificationException("Authenticator metadata statement has invalid hash"); - if (null != entry.MetadataStatement) + + var hasBasicFull = entry.MetadataStatement.AttestationTypes.Contains((ushort)MetadataAttestationType.ATTESTATION_BASIC_FULL); + if (false == hasBasicFull && + null != trustPath && + trustPath.FirstOrDefault().Subject != trustPath.FirstOrDefault().Issuer) throw new Fido2VerificationException("Attestation with full attestation from authentictor that does not support full attestation"); + if (true == hasBasicFull && null != trustPath && trustPath.FirstOrDefault().Subject != trustPath.FirstOrDefault().Issuer) { - var hasBasicFull = entry.MetadataStatement.AttestationTypes.Contains((ushort)MetadataAttestationType.ATTESTATION_BASIC_FULL); - if (false == hasBasicFull && - null != trustPath && trustPath.FirstOrDefault().Subject != trustPath.FirstOrDefault().Issuer) throw new Fido2VerificationException("Attestation with full attestation from authentictor that does not support full attestation"); - if (true == hasBasicFull && null != trustPath && trustPath.FirstOrDefault().Subject != trustPath.FirstOrDefault().Issuer) + var root = new X509Certificate2(Convert.FromBase64String(entry.MetadataStatement.AttestationRootCertificates.FirstOrDefault())); + var chain = new X509Chain(); + chain.ChainPolicy.ExtraStore.Add(root); + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; + if (trustPath.Length > 1) { - var root = new X509Certificate2(Convert.FromBase64String(entry.MetadataStatement.AttestationRootCertificates.FirstOrDefault())); - var chain = new X509Chain(); - chain.ChainPolicy.ExtraStore.Add(root); - if (trustPath.Length > 1) + foreach (var cert in trustPath.Skip(1).Reverse()) { - foreach (X509Certificate2 cert in trustPath.Skip(1).Reverse()) - { - chain.ChainPolicy.ExtraStore.Add(cert); - } + chain.ChainPolicy.ExtraStore.Add(cert); } - var valid = chain.Build(trustPath[0]); - if (false == valid) - { + } + var valid = chain.Build(trustPath[0]); + if (false == valid) + { - } } } - foreach (StatusReport report in entry.StatusReports) + foreach (var report in entry.StatusReports) { if (true == Enum.IsDefined(typeof(UndesiredAuthenticatorStatus), (UndesiredAuthenticatorStatus) report.Status)) throw new Fido2VerificationException("Authenticator found with undesirable status"); } diff --git a/fido2-net-lib/MetadataService.cs b/fido2-net-lib/MetadataService.cs index 6f519235..b378288c 100644 --- a/fido2-net-lib/MetadataService.cs +++ b/fido2-net-lib/MetadataService.cs @@ -534,24 +534,57 @@ public void CustomTOCPayloadFromCache() { if (true == System.IO.Directory.Exists(_cacheDir + @"\Custom")) { - foreach (string filename in System.IO.Directory.GetFiles(_cacheDir + @"\Custom")) + foreach (var filename in System.IO.Directory.GetFiles(_cacheDir + @"\Custom")) { var rawStatement = System.IO.File.ReadAllText(filename); var statement = JsonConvert.DeserializeObject(rawStatement); - var entry = new MetadataTOCPayloadEntry(); - entry.AaGuid = statement.AaGuid; - entry.MetadataStatement = statement; - entry.StatusReports = new StatusReport[] { new StatusReport() { Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED } }; + var entry = new MetadataTOCPayloadEntry + { + AaGuid = statement.AaGuid, + MetadataStatement = statement, + StatusReports = new StatusReport[] { new StatusReport() { Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED } } + }; if (null != entry.AaGuid) payload.Add(new System.Guid(entry.AaGuid), entry); } } else { - var entry = new MetadataTOCPayloadEntry(); - entry.AaGuid = "2b2ecbb4-59b4-44fa-868d-a072485d8ae0"; - entry.StatusReports = new StatusReport[] { new StatusReport() { Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED } }; - entry.MetadataStatement = new MetadataStatement() { AttestationTypes = new ushort[] { (ushort) MetadataAttestationType.ATTESTATION_BASIC_FULL } }; + var entry = new MetadataTOCPayloadEntry + { + AaGuid = "2b2ecbb4-59b4-44fa-868d-a072485d8ae0", + Hash = "", + StatusReports = new StatusReport[] { new StatusReport() { Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED } }, + MetadataStatement = new MetadataStatement() { AttestationTypes = new ushort[] { (ushort)MetadataAttestationType.ATTESTATION_BASIC_FULL }, Hash = "" } + }; payload.Add(new System.Guid(entry.AaGuid), entry); + + // from https://developers.yubico.com/U2F/yubico-u2f-ca-certs.txt + var yubicoRoot = "MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ" + + "dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw" + + "MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290" + + "IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK" + + "AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk" + + "5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep" + + "8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw" + + "nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT" + + "9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw" + + "LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ" + + "hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN" + + "BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4" + + "MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt" + + "hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k" + + "LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U" + + "sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc" + + "U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw=="; + + var yubico = new MetadataTOCPayloadEntry + { + AaGuid = "f8a011f3-8c0a-4d15-8006-17111f9edc7d", + Hash = "", + StatusReports = new StatusReport[] { new StatusReport() { Status = AuthenticatorStatus.NOT_FIDO_CERTIFIED } }, + MetadataStatement = new MetadataStatement() { AttestationTypes = new ushort[] { (ushort)MetadataAttestationType.ATTESTATION_BASIC_FULL }, Hash = "", AttestationRootCertificates = new string[] { yubicoRoot } } + }; + payload.Add(new System.Guid(yubico.AaGuid), yubico); } } From a422dd6ab0699bc800e845768332dbc6b2c7ba0f Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Sun, 21 Oct 2018 11:58:09 -0400 Subject: [PATCH 06/10] Prevent exception on first run If unconfigured, continue silently. --- fido2-net-lib/MetadataService.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/fido2-net-lib/MetadataService.cs b/fido2-net-lib/MetadataService.cs index b378288c..a23720c4 100644 --- a/fido2-net-lib/MetadataService.cs +++ b/fido2-net-lib/MetadataService.cs @@ -323,23 +323,9 @@ public sealed class MDSMetadata : IMetadataService private MDSMetadata(string accessToken, string cachedirPath) { - //// Extract app secrets for development - //// https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.1&tabs=windows - //string env = System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - //if (string.IsNullOrWhiteSpace(env)) - //{ - // env = "Development"; - //} - - //var builder = new Microsoft.Extensions.Configuration.ConfigurationBuilder(); - - //if (env == "Development") - //{ - // builder.AddUserSecrets(); - //} - //Configuration = builder.Build(); - // We need either an access token or a cache directory, but prefer both + if (null == accessToken && null == cachedirPath) return; + // If we have only an access token, we can get metadata from directly from MDS and only cache in memory // If we have only a cache directory, we can read cached data (as long as it is not expired) // If we have both, we can read from either and update cache as necessary From 1ef8ffcea47d1d61e7acd4c8c8ea471119cebda8 Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Sun, 21 Oct 2018 14:06:23 -0400 Subject: [PATCH 07/10] Update Chaos.NaCl.csproj --- Chaos.NaCl/Chaos.NaCl.csproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Chaos.NaCl/Chaos.NaCl.csproj b/Chaos.NaCl/Chaos.NaCl.csproj index 5c67ef1b..65b0a5f9 100644 --- a/Chaos.NaCl/Chaos.NaCl.csproj +++ b/Chaos.NaCl/Chaos.NaCl.csproj @@ -9,7 +9,7 @@ Properties Chaos.NaCl Chaos.NaCl - v2.0 + v4.6.1 512 @@ -24,6 +24,7 @@ 4 false SecurityRules.ruleset + false pdbonly @@ -34,6 +35,7 @@ 4 default MinimumRecommendedRules.ruleset + false false @@ -114,4 +116,4 @@ --> - \ No newline at end of file + From c8bdead56668795bbc6e6d4b677e12414838fb0a Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Tue, 23 Oct 2018 23:45:25 -0400 Subject: [PATCH 08/10] Move attestation format specific code out to their own files, simplify and improve readability --- fido2-net-lib/AssertionOptions.cs | 5 +- fido2-net-lib/AttestationFormat/AndroidKey.cs | 240 +++++++++ .../AttestationFormat/AndroidSafetyNet.cs | 99 ++++ .../AttestationFormat/AttestationFormat.cs | 37 ++ fido2-net-lib/AttestationFormat/FidoU2f.cs | 73 +++ fido2-net-lib/AttestationFormat/None.cs | 24 + fido2-net-lib/AttestationFormat/Packed.cs | 130 +++++ fido2-net-lib/AttestationFormat/Tpm.cs | 155 ++++++ fido2-net-lib/AuthDataHelper.cs | 429 +-------------- .../AuthenticatorAssertionResponse.cs | 4 +- .../AuthenticatorAttestationResponse.cs | 491 +++--------------- fido2-net-lib/CryptoUtils.cs | 291 +++++++++++ fido2-net-lib/MetadataService.cs | 7 +- .../AttestationFormatVerificationResult.cs | 10 + 14 files changed, 1132 insertions(+), 863 deletions(-) create mode 100644 fido2-net-lib/AttestationFormat/AndroidKey.cs create mode 100644 fido2-net-lib/AttestationFormat/AndroidSafetyNet.cs create mode 100644 fido2-net-lib/AttestationFormat/AttestationFormat.cs create mode 100644 fido2-net-lib/AttestationFormat/FidoU2f.cs create mode 100644 fido2-net-lib/AttestationFormat/None.cs create mode 100644 fido2-net-lib/AttestationFormat/Packed.cs create mode 100644 fido2-net-lib/AttestationFormat/Tpm.cs create mode 100644 fido2-net-lib/CryptoUtils.cs create mode 100644 fido2-net-lib/Objects/AttestationFormatVerificationResult.cs diff --git a/fido2-net-lib/AssertionOptions.cs b/fido2-net-lib/AssertionOptions.cs index 18862999..41cb2969 100644 --- a/fido2-net-lib/AssertionOptions.cs +++ b/fido2-net-lib/AssertionOptions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Fido2NetLib.Objects; using Newtonsoft.Json; @@ -65,7 +64,7 @@ public static AssertionOptions FromJson(string json) return JsonConvert.DeserializeObject(json); } - // todo: Add Extensions + // TODO: Add Extensions } diff --git a/fido2-net-lib/AttestationFormat/AndroidKey.cs b/fido2-net-lib/AttestationFormat/AndroidKey.cs new file mode 100644 index 00000000..07e96845 --- /dev/null +++ b/fido2-net-lib/AttestationFormat/AndroidKey.cs @@ -0,0 +1,240 @@ + +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Fido2NetLib.Objects; +using PeterO.Cbor; + +namespace Fido2NetLib.AttestationFormat +{ + class AndroidKey : AttestationFormat + { + public static byte[] GetASN1ObjectAtIndex(byte[] attExtBytes, int index) + { + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // This function returns an entry from the KeyDescription at index + if (null == attExtBytes || 0 == attExtBytes.Length || attExtBytes.Length > UInt16.MaxValue) throw new Fido2VerificationException("Invalid attExtBytes signature value"); + var offset = 0; + var derSequence = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + // expecting to start with 0x30 indicating SEQUENCE + if (null == derSequence || 0x30 != derSequence[0]) throw new Fido2VerificationException("attExtBytes signature not a valid DER sequence"); + // next is length of all the items in the sequence + var dataLen = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + if (null == dataLen) throw new Fido2VerificationException("attExtBytes signature has invalid length"); + // if data is more than 127 bytes, the length is encoded in long form + var longForm = (dataLen[0] > 0x7f); + var longLen = 0; + if (true == longForm) + { + var longLenByte = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + if (null == longLenByte) throw new Fido2VerificationException("attExtBytes signature has invalid long form length"); + longLen = longLenByte[0]; + longLen &= (1 << 7); + } + + // walk through each sequence entry until we get to the requested index + for (var i = 0; i < index; i++) + { + var derId = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + if (null == derId) throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); + var lenValue = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + if (null == lenValue) throw new Fido2VerificationException("attExtBytes lenValue invalid"); + if (0 < lenValue[0]) + { + var value = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, lenValue[0]); + if (null == value) throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); + } + } + // skip the identifier of the requested item + var asn1Id = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + if (null == asn1Id) throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); + // get length of requested item + var lenAsn1value = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); + if (null == lenAsn1value) throw new Fido2VerificationException("lenAttestationChallenge version length invalid"); + // return byte array containing the requested item + return AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, lenAsn1value[0]); + } + public static byte[] GetAttestationChallenge(byte[] attExtBytes) + { + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // attestationChallenge at index 4 + return GetASN1ObjectAtIndex(attExtBytes, 4); + } + public static byte[] GetSoftwareEnforcedAuthorizationList(byte[] attExtBytes) + { + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // softwareEnforced AuthorizationList at index 6 + return GetASN1ObjectAtIndex(attExtBytes, 6); + } + public static byte[] GetTeeEnforcedAuthorizationList(byte[] attExtBytes) + { + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // teeEnforced AuthorizationList at index 7 + return GetASN1ObjectAtIndex(attExtBytes, 7); + } + + public static bool FindAllApplicationsField(byte[] attExtBytes) + { + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // check both software and tee enforced AuthorizationList objects for presense of "allApplications" tag, number 600 + var software = GetSoftwareEnforcedAuthorizationList(attExtBytes); + var tee = GetTeeEnforcedAuthorizationList(attExtBytes); + var ignore = -1; + // allApplications is tag 600, and should not be found in either list + return (GetDERTagValue(software, 600, ref ignore) && GetDERTagValue(tee, 600, ref ignore)); + } + public static bool GetDERTagValue(byte[] authList, int tag, ref int result) + { + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // walk authorizationList sequence looking for an item with requested tag + // if tag is found, return true and set the result value to the found int value + // the only two items we are expecting to find are purpose and origin + // which are set of integer or integer, so int result is ok for now + // if entire list is walked and tag is not found, return false + for (int i = 0; i < authList.Length;) + { + // expecting to see first byte indicting the attribute is of class 2, and constructed value + // first two bits are the class, expecting to see 10 + // third bit is primative (0) or constructed (1) + if (false == ((authList[i] & 0xA0) == 0xA0)) throw new Fido2VerificationException("Expected class 2 constructed ASN.1 value"); + var foundTag = 0; + // if the tag value is below 0x1F (11111), the value is stored in the remaining 5 bits of the first byte + if (false == ((authList[i] & 0x1F) == 0x1F)) + { + foundTag = (authList[i] & ~0xA0); + i++; + } + // otherwise, if the tag value is 0x3FFF (11111111111111) or below + // the value is stored in the lower 7 bits of the second byte, and the lower 7 bits of the third byte + // this is signified by the high order bit set in the second byte, but not set in the third byte + else if (((authList[i + 1] & 0x80) == 0x80) && ((authList[i + 2] & 0x80) == 0x0)) + { + // take the lower 7 bits in the second byte, shift them left 7 positions + // then add the lower 7 bits of the third byte to get the tag value + // Welcome to Abstract Syntax Notation One + foundTag = ((authList[i + 1] & ~0x80) << 7) + (authList[i + 2]); + i += 3; + } + else throw new Fido2VerificationException("Expecting ASN.1 tag less than 0x3FFF"); + // if the tag we found is the one that we are looking for, get the value + if (tag == foundTag) + { + // 5 bytes will be remaining if this a set (0x31), advance to the integer + if (5 == authList[i] && 0x31 == authList[i + 1]) i += 2; + // for our purposes, we should see that there are 3 bytes remaining after this one + // the second byte should be 2 indicating the value is an integer + // and the third byte is the length in bytes, which again, for our purposes, should be one + if (3 == authList[i] && 2 == authList[i + 1] && 1 == authList[i + 2]) + { + // value is stored in the 4th byte, no need to go any further, we have our result + result = authList[i + 3]; + return true; + } + else throw new Fido2VerificationException("Unexpected byte sequence found fetching ASN.1 integer value"); + } + // if we didn't find the tag we were looking for, advance the index + // by the the size in bytes of the current tag plus one byte for the size + else if (i < authList.Length) i += authList[i] + 1; + } + // ran out of bytes without finding the tag we were looking for + return false; + } + + public static bool IsOriginGenerated(byte[] attExtBytes) + { + var tagValue = -1; + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // origin tag is 702 + var result = GetDERTagValue(GetTeeEnforcedAuthorizationList(attExtBytes), 702, ref tagValue); + return (0 == tagValue && true == result); + } + public static bool IsPurposeSign(byte[] attExtBytes) + { + var tagValue = -1; + // https://developer.android.com/training/articles/security-key-attestation#certificate_schema + // purpose tag is 1 + var result = GetDERTagValue(GetTeeEnforcedAuthorizationList(attExtBytes), 1, ref tagValue); + return (2 == tagValue && true == result); + } + public AndroidKey(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) + { + } + public override AttestationFormatVerificationResult Verify() + { + // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields + if (0 == attStmt.Keys.Count || 0 == attStmt.Values.Count) + throw new Fido2VerificationException("Attestation format packed must have attestation statement"); + + if (null == Sig || CBORType.ByteString != Sig.Type || 0 == Sig.GetByteString().Length) + throw new Fido2VerificationException("Invalid packed attestation signature"); + // 2a. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash + // using the attestation public key in attestnCert with the algorithm specified in alg + if (null == X5c && CBORType.Array != X5c.Type && 0 == X5c.Count) + throw new Fido2VerificationException("Malformed x5c in android-key attestation"); + + if (null == X5c.Values || 0 == X5c.Values.Count || + CBORType.ByteString != X5c.Values.First().Type || + 0 == X5c.Values.First().GetByteString().Length) + throw new Fido2VerificationException("Malformed x5c in android-key attestation"); + + X509Certificate2 androidKeyCert = null; + ECDsaCng androidKeyPubKey = null; + try + { + androidKeyCert = new X509Certificate2(X5c.Values.First().GetByteString()); + androidKeyPubKey = (ECDsaCng)androidKeyCert.GetECDsaPublicKey(); // attestation public key + } + catch (Exception ex) + { + throw new Fido2VerificationException("Failed to extract public key from android key" + ex.Message); + } + + if (null == Alg || CBORType.Number != Alg.Type || false == CryptoUtils.algMap.ContainsKey(Alg.AsInt32())) throw new Fido2VerificationException("Invalid attestation algorithm"); + if (true != androidKeyPubKey.VerifyData(Data, CryptoUtils.SigFromEcDsaSig(Sig.GetByteString()), CryptoUtils.algMap[Alg.AsInt32()])) throw new Fido2VerificationException("Invalid android key signature"); + + var credentialPublicKey = CBORObject.DecodeFromBytes(AuthData.AttData.CredentialPublicKey); + var cng = ECDsa.Create(new ECParameters + { + Curve = ECCurve.NamedCurves.nistP256, + Q = new ECPoint + { + + X = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(), + Y = credentialPublicKey[CBORObject.FromObject(-3)].GetByteString() + } + }); + // Verify that the public key in the first certificate in in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData. + if (true != cng.VerifyData(Data, CryptoUtils.SigFromEcDsaSig(Sig.GetByteString()), CryptoUtils.algMap[Alg.AsInt32()])) + throw new Fido2VerificationException("Invalid android key signature"); + + // Verify that in the attestation certificate extension data: + var attExtBytes = AuthDataHelper.AttestationExtensionBytes(androidKeyCert.Extensions); + + // 1. The value of the attestationChallenge field is identical to clientDataHash. + var attestationChallenge = GetAttestationChallenge(attExtBytes); + if (false == clientDataHash.SequenceEqual(attestationChallenge)) + throw new Fido2VerificationException("Mismatched between attestationChallenge and hashedClientDataJson verifying android key attestation certificate extension"); + + // 2. The AuthorizationList.allApplications field is not present, since PublicKeyCredential MUST be bound to the RP ID. + if (true == FindAllApplicationsField(attExtBytes)) + throw new Fido2VerificationException("Found all applications field in android key attestation certificate extension"); + + // 3. The value in the AuthorizationList.origin field is equal to KM_ORIGIN_GENERATED ( which == 0). + if (false == IsOriginGenerated(attExtBytes)) + throw new Fido2VerificationException("Found origin field not set to KM_ORIGIN_GENERATED in android key attestation certificate extension"); + + // 4. The value in the AuthorizationList.purpose field is equal to KM_PURPOSE_SIGN (which == 2). + if (false == IsPurposeSign(attExtBytes)) + throw new Fido2VerificationException("Found purpose field not set to KM_PURPOSE_SIGN in android key attestation certificate extension"); + + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.Basic, + trustPath = X5c.Values + .Select(x => new X509Certificate2(x.GetByteString())) + .ToArray() + }; + } + } +} diff --git a/fido2-net-lib/AttestationFormat/AndroidSafetyNet.cs b/fido2-net-lib/AttestationFormat/AndroidSafetyNet.cs new file mode 100644 index 00000000..1c5b035a --- /dev/null +++ b/fido2-net-lib/AttestationFormat/AndroidSafetyNet.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Fido2NetLib.Objects; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; +using PeterO.Cbor; + +namespace Fido2NetLib.AttestationFormat +{ + class AndroidSafetyNet : AttestationFormat + { + public AndroidSafetyNet(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) + { + } + public override AttestationFormatVerificationResult Verify() + { + + // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform + // CBOR decoding on it to extract the contained fields + if ((CBORType.TextString != attStmt["ver"].Type) || + (0 == attStmt["ver"].AsString().Length)) + throw new Fido2VerificationException("Invalid version in SafetyNet data"); + + // Verify that response is a valid SafetyNet response of version ver + var ver = attStmt["ver"].AsString(); + + if ((CBORType.ByteString != attStmt["response"].Type) || + (0 == attStmt["response"].GetByteString().Length)) + throw new Fido2VerificationException("Invalid response in SafetyNet data"); + + var response = attStmt["response"].GetByteString(); + var signedAttestationStatement = Encoding.UTF8.GetString(response); + var jwtToken = new JwtSecurityToken(signedAttestationStatement); + X509SecurityKey[] keys = (jwtToken.Header["x5c"] as JArray) + .Values() + .Select(x => new X509SecurityKey( + new X509Certificate2(Convert.FromBase64String(x)))) + .ToArray(); + if ((null == keys) || (0 == keys.Count())) throw new Fido2VerificationException("SafetyNet attestation missing x5c"); + var validationParameters = new TokenValidationParameters + { + ValidateIssuer = false, + ValidateAudience = false, + ValidateLifetime = false, + ValidateIssuerSigningKey = true, + IssuerSigningKeys = keys + }; + + var tokenHandler = new JwtSecurityTokenHandler(); + SecurityToken validatedToken; + + tokenHandler.ValidateToken( + signedAttestationStatement, + validationParameters, + out validatedToken); + + if (false == (validatedToken.SigningKey is X509SecurityKey)) throw new Fido2VerificationException("Safetynet signing key invalid"); + + var nonce = ""; + var payload = false; + foreach (var claim in jwtToken.Claims) + { + if (("nonce" == claim.Type) && ("http://www.w3.org/2001/XMLSchema#string" == claim.ValueType) && (0 != claim.Value.Length)) nonce = claim.Value; + if (("ctsProfileMatch" == claim.Type) && ("http://www.w3.org/2001/XMLSchema#boolean" == claim.ValueType)) + { + payload = bool.Parse(claim.Value); + } + if (("timestampMs" == claim.Type) && ("http://www.w3.org/2001/XMLSchema#integer64" == claim.ValueType)) + { + var dt = DateTimeHelper.UnixEpoch.AddMilliseconds(double.Parse(claim.Value)); + if ((DateTime.UtcNow < dt) || (DateTime.UtcNow.AddMinutes(-1) > dt)) throw new Fido2VerificationException("Android SafetyNet timestampMs must be between one minute ago and now"); + } + } + + // Verify that the nonce in the response is identical to the SHA-256 hash of the concatenation of authenticatorData and clientDataHash + if ("" == nonce) throw new Fido2VerificationException("Nonce value not found in Android SafetyNet attestation"); + if (!CryptoUtils.GetHasher(HashAlgorithmName.SHA256).ComputeHash(Data).SequenceEqual(Convert.FromBase64String(nonce))) throw new Fido2VerificationException("Android SafetyNet hash value mismatch"); + + // Verify that the attestation certificate is issued to the hostname "attest.android.com" + if (false == ("attest.android.com").Equals((validatedToken.SigningKey as X509SecurityKey).Certificate.GetNameInfo(X509NameType.DnsName, false))) throw new Fido2VerificationException("Safetynet DnsName is not attest.android.com"); + + // Verify that the ctsProfileMatch attribute in the payload of response is true + if (true != payload) throw new Fido2VerificationException("Android SafetyNet ctsProfileMatch must be true"); + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.Basic, + trustPath = (jwtToken.Header["x5c"] as JArray) + .Values() + .Select(x => new X509Certificate2(Convert.FromBase64String(x))) + .ToArray() + }; + } + } +} diff --git a/fido2-net-lib/AttestationFormat/AttestationFormat.cs b/fido2-net-lib/AttestationFormat/AttestationFormat.cs new file mode 100644 index 00000000..cb0efa91 --- /dev/null +++ b/fido2-net-lib/AttestationFormat/AttestationFormat.cs @@ -0,0 +1,37 @@ +using PeterO.Cbor; +using Fido2NetLib.Objects; +using System; + +namespace Fido2NetLib.AttestationFormat +{ + public abstract class AttestationFormat + { + public AttestationFormat(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) + { + this.attStmt = attStmt; + this.authenticatorData = authenticatorData; + this.clientDataHash = clientDataHash; + } + public CBORObject attStmt; + public byte[] authenticatorData; + public byte[] clientDataHash; + internal CBORObject Sig { get { return attStmt["sig"]; } } + internal CBORObject X5c { get { return attStmt["x5c"]; } } + internal CBORObject Alg { get { return attStmt["alg"]; } } + internal CBORObject EcdaaKeyId { get { return attStmt["ecdaaKeyId"]; } } + internal AuthenticatorData AuthData { get { return new AuthenticatorData(authenticatorData); } } + internal CBORObject CredentialPublicKey { get {return CBORObject.DecodeFromBytes(AuthData.AttData.CredentialPublicKey); } } + internal byte[] Data + { + get + { + byte[] data = new byte[authenticatorData.Length + clientDataHash.Length]; + Buffer.BlockCopy(authenticatorData, 0, data, 0, authenticatorData.Length); + Buffer.BlockCopy(clientDataHash, 0, data, authenticatorData.Length, clientDataHash.Length); + return data; + } + } + + public abstract AttestationFormatVerificationResult Verify(); + } +} diff --git a/fido2-net-lib/AttestationFormat/FidoU2f.cs b/fido2-net-lib/AttestationFormat/FidoU2f.cs new file mode 100644 index 00000000..2b6a32e9 --- /dev/null +++ b/fido2-net-lib/AttestationFormat/FidoU2f.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Fido2NetLib.Objects; +using PeterO.Cbor; + +namespace Fido2NetLib.AttestationFormat +{ + class FidoU2f : AttestationFormat + { + public FidoU2f(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) + { + } + public override AttestationFormatVerificationResult Verify() + { + // verify that aaguid is 16 empty bytes (note: required by fido2 conformance testing, could not find this in spec?) + if (false == AuthData.AttData.Aaguid.SequenceEqual(Guid.Empty.ToByteArray())) + throw new Fido2VerificationException("Aaguid was not empty parsing fido-u2f atttestation statement"); + + // 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. + if (null == X5c || CBORType.Array != X5c.Type || X5c.Count != 1) + throw new Fido2VerificationException("Malformed x5c in fido - u2f attestation"); + + // 2a. the attestation certificate attestnCert MUST be the first element in the array + if (null == X5c.Values || 0 == X5c.Values.Count || + CBORType.ByteString != X5c.Values.First().Type || + 0 == X5c.Values.First().GetByteString().Length) + throw new Fido2VerificationException("Malformed x5c in fido-u2f attestation"); + + var cert = new X509Certificate2(X5c.Values.First().GetByteString()); + + // 2b. If certificate public key is not an Elliptic Curve (EC) public key over the P-256 curve, terminate this algorithm and return an appropriate error + var pubKey = (ECDsaCng)cert.GetECDsaPublicKey(); + if (CngAlgorithm.ECDsaP256 != pubKey.Key.Algorithm) + throw new Fido2VerificationException("Attestation certificate public key is not an Elliptic Curve (EC) public key over the P-256 curve"); + + // 3. Extract the claimed rpIdHash from authenticatorData, and the claimed credentialId and credentialPublicKey from authenticatorData + // see rpIdHash, credentialId, and credentialPublicKey variables + + // 4. Convert the COSE_KEY formatted credentialPublicKey (see Section 7 of [RFC8152]) to CTAP1/U2F public Key format + var publicKeyU2F = CryptoUtils.U2FKeyFromCOSEKey(CredentialPublicKey); + + // 5. Let verificationData be the concatenation of (0x00 || rpIdHash || clientDataHash || credentialId || publicKeyU2F) + var verificationData = new byte[1] { 0x00 }; + verificationData = verificationData + .Concat(AuthData.RpIdHash) + .Concat(clientDataHash) + .Concat(AuthData.AttData.CredentialID) + .Concat(publicKeyU2F.ToArray()) + .ToArray(); + + // 6. Verify the sig using verificationData and certificate public key + if (null == Sig || CBORType.ByteString != Sig.Type || 0 == Sig.GetByteString().Length) + throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); + + var ecsig = CryptoUtils.SigFromEcDsaSig(Sig.GetByteString()); + if (null == ecsig) + throw new Fido2VerificationException("Failed to decode fido-u2f attestation signature from ASN.1 encoded form"); + + if (true != pubKey.VerifyData(verificationData, ecsig, CryptoUtils.algMap[CredentialPublicKey[CBORObject.FromObject(3)].AsInt32()])) + throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); + + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.None, + trustPath = null + }; + } + } +} diff --git a/fido2-net-lib/AttestationFormat/None.cs b/fido2-net-lib/AttestationFormat/None.cs new file mode 100644 index 00000000..260bc217 --- /dev/null +++ b/fido2-net-lib/AttestationFormat/None.cs @@ -0,0 +1,24 @@ +using Fido2NetLib.Objects; +using PeterO.Cbor; + +namespace Fido2NetLib.AttestationFormat +{ + class None : AttestationFormat + { + public None(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) + { + } + + public override AttestationFormatVerificationResult Verify() + { + if (0 != attStmt.Keys.Count && 0 != attStmt.Values.Count) + throw new Fido2VerificationException("Attestation format none should have no attestation statement"); + + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.None, + trustPath = null + }; + } + } +} diff --git a/fido2-net-lib/AttestationFormat/Packed.cs b/fido2-net-lib/AttestationFormat/Packed.cs new file mode 100644 index 00000000..395aa58c --- /dev/null +++ b/fido2-net-lib/AttestationFormat/Packed.cs @@ -0,0 +1,130 @@ +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Fido2NetLib.Objects; +using PeterO.Cbor; + +namespace Fido2NetLib.AttestationFormat +{ + class Packed : AttestationFormat + { + public Packed(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) + { + } + public override AttestationFormatVerificationResult Verify() + { + // Verify that attStmt is valid CBOR conforming to the syntax defined above and + // perform CBOR decoding on it to extract the contained fields. + if (0 == attStmt.Keys.Count || 0 == attStmt.Values.Count) + throw new Fido2VerificationException("Attestation format packed must have attestation statement"); + + if (null == Sig || CBORType.ByteString != Sig.Type || 0 == Sig.GetByteString().Length) + throw new Fido2VerificationException("Invalid packed attestation signature"); + + if (null == Alg || CBORType.Number != Alg.Type) + throw new Fido2VerificationException("Invalid packed attestation algorithm"); + + // If x5c is present, this indicates that the attestation type is not ECDAA + if (null != X5c) + { + if (CBORType.Array != X5c.Type || 0 == X5c.Count || null != EcdaaKeyId) + throw new Fido2VerificationException("Malformed x5c array in packed attestation statement"); + var enumerator = X5c.Values.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (null == enumerator || null == enumerator.Current + || CBORType.ByteString != enumerator.Current.Type + || 0 == enumerator.Current.GetByteString().Length) + throw new Fido2VerificationException("Malformed x5c cert found in packed attestation statement"); + + var x5ccert = new X509Certificate2(enumerator.Current.GetByteString()); + + if (DateTime.UtcNow < x5ccert.NotBefore || DateTime.UtcNow > x5ccert.NotAfter) + throw new Fido2VerificationException("Packed signing certificate expired or not yet valid"); + } + + // The attestation certificate attestnCert MUST be the first element in the array. + var attestnCert = new X509Certificate2(X5c.Values.First().GetByteString()); + + // 2a. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash + // using the attestation public key in attestnCert with the algorithm specified in alg + var packedPubKey = (ECDsaCng)attestnCert.GetECDsaPublicKey(); // attestation public key + if (false == CryptoUtils.algMap.ContainsKey(Alg.AsInt32())) + throw new Fido2VerificationException("Invalid attestation algorithm"); + + var coseKey = CryptoUtils.CoseKeyFromCertAndAlg(attestnCert, Alg.AsInt32()); + + if (true != CryptoUtils.VerifySigWithCoseKey(Data, coseKey, Sig.GetByteString())) + throw new Fido2VerificationException("Invalid full packed signature"); + + // Verify that attestnCert meets the requirements in https://www.w3.org/TR/webauthn/#packed-attestation-cert-requirements + // 2b. Version MUST be set to 3 + if (3 != attestnCert.Version) + throw new Fido2VerificationException("Packed x5c attestation certificate not V3"); + + // Subject field MUST contain C, O, OU, CN + // OU must match "Authenticator Attestation" + if (true != AuthDataHelper.IsValidPackedAttnCertSubject(attestnCert.Subject)) + throw new Fido2VerificationException("Invalid attestation cert subject"); + + // 2c. If the related attestation root certificate is used for multiple authenticator models, + // the Extension OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) MUST be present, containing the AAGUID as a 16-byte OCTET STRING + // verify that the value of this extension matches the aaguid in authenticatorData + var aaguid = AuthDataHelper.AaguidFromAttnCertExts(attestnCert.Extensions); + if (aaguid != null && !aaguid.SequenceEqual(AuthData.AttData.Aaguid.ToArray())) + throw new Fido2VerificationException("aaguid present in packed attestation but does not match aaguid from authData"); + + // 2d. The Basic Constraints extension MUST have the CA component set to false + if (AuthDataHelper.IsAttnCertCACert(attestnCert.Extensions)) + throw new Fido2VerificationException("Attestion certificate has CA cert flag present"); + + // id-fido-u2f-ce-transports + var u2ftransports = AuthDataHelper.U2FTransportsFromAttnCert(attestnCert.Extensions); + + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.Basic, + trustPath = X5c.Values + .Select(x => new X509Certificate2(x.GetByteString())) + .ToArray() + }; + + } + // If ecdaaKeyId is present, then the attestation type is ECDAA + else if (null != EcdaaKeyId) + { + // Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash + // using ECDAA-Verify with ECDAA-Issuer public key identified by ecdaaKeyId + // https://www.w3.org/TR/webauthn/#biblio-fidoecdaaalgorithm + + throw new Fido2VerificationException("ECDAA is not yet implemented"); + // If successful, return attestation type ECDAA and attestation trust path ecdaaKeyId. + //attnType = AttestationType.ECDAA; + //trustPath = ecdaaKeyId; + } + // If neither x5c nor ecdaaKeyId is present, self attestation is in use + else + { + // Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData + var credentialPublicKey = CBORObject.DecodeFromBytes(AuthData.AttData.CredentialPublicKey); + var coseAlg = credentialPublicKey[CBORObject.FromObject(3)].AsInt32(); + if (Alg.AsInt32() != coseAlg) + throw new Fido2VerificationException("Algorithm mismatch between credential public key and authenticator data in self attestation statement"); + + // Verify that sig is a valid signature over the concatenation of authenticatorData and + // clientDataHash using the credential public key with alg + + if (true != CryptoUtils.VerifySigWithCoseKey(Data, credentialPublicKey, Sig.GetByteString())) + throw new Fido2VerificationException("Failed to validate signature"); + + // If successful, return attestation type Self and empty attestation trust path. + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.Self, + trustPath = null + }; + } + } + } +} diff --git a/fido2-net-lib/AttestationFormat/Tpm.cs b/fido2-net-lib/AttestationFormat/Tpm.cs new file mode 100644 index 00000000..2d966cf9 --- /dev/null +++ b/fido2-net-lib/AttestationFormat/Tpm.cs @@ -0,0 +1,155 @@ +using System; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Fido2NetLib.Objects; +using PeterO.Cbor; + +namespace Fido2NetLib.AttestationFormat +{ + class Tpm : AttestationFormat + { + public Tpm(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) + { + } + public override AttestationFormatVerificationResult Verify() + { + if (null == Sig || CBORType.ByteString != Sig.Type || 0 == Sig.GetByteString().Length) + throw new Fido2VerificationException("Invalid TPM attestation signature"); + + if ("2.0" != attStmt["ver"].AsString()) + throw new Fido2VerificationException("FIDO2 only supports TPM 2.0"); + + // Verify that the public key specified by the parameters and unique fields of pubArea + // is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData + PubArea pubArea = null; + if (null != attStmt["pubArea"] && + CBORType.ByteString == attStmt["pubArea"].Type && + 0 != attStmt["pubArea"].GetByteString().Length) + pubArea = new PubArea(attStmt["pubArea"].GetByteString()); + + if (null == pubArea || null == pubArea.Unique || 0 == pubArea.Unique.Length) + throw new Fido2VerificationException("Missing or malformed pubArea"); + + var coseKty = CredentialPublicKey[CBORObject.FromObject(1)].AsInt32(); + if (3 == coseKty) // RSA + { + var coseMod = CredentialPublicKey[CBORObject.FromObject(-1)].GetByteString(); // modulus + var coseExp = CredentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); // exponent + + if (!coseMod.ToArray().SequenceEqual(pubArea.Unique.ToArray())) throw new Fido2VerificationException("Public key mismatch between pubArea and credentialPublicKey"); + if ((coseExp[0] + (coseExp[1] << 8) + (coseExp[2] << 16)) != pubArea.Exponent) throw new Fido2VerificationException("Public key exponent mismatch between pubArea and credentialPublicKey"); + } + else if (2 == coseKty) // ECC + { + var curve = CredentialPublicKey[CBORObject.FromObject(-1)].AsInt32(); + var X = CredentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); + var Y = CredentialPublicKey[CBORObject.FromObject(-3)].GetByteString(); + + if (pubArea.EccCurve != CryptoUtils.CoseCurveToTpm[curve]) throw new Fido2VerificationException("Curve mismatch between pubArea and credentialPublicKey"); + if (!pubArea.ECPoint.X.SequenceEqual(X)) throw new Fido2VerificationException("X-coordinate mismatch between pubArea and credentialPublicKey"); + if (!pubArea.ECPoint.Y.SequenceEqual(Y)) throw new Fido2VerificationException("Y-coordinate mismatch between pubArea and credentialPublicKey"); + } + // Concatenate authenticatorData and clientDataHash to form attToBeSigned. + // see data variable + + // Validate that certInfo is valid + CertInfo certInfo = null; + if (null != attStmt["certInfo"] && + CBORType.ByteString == attStmt["certInfo"].Type && + 0 != attStmt["certInfo"].GetByteString().Length) + certInfo = new CertInfo(attStmt["certInfo"].GetByteString()); + + if (null == certInfo || null == certInfo.ExtraData || 0 == certInfo.ExtraData.Length) + throw new Fido2VerificationException("CertInfo invalid parsing TPM format attStmt"); + + // Verify that magic is set to TPM_GENERATED_VALUE and type is set to TPM_ST_ATTEST_CERTIFY + // handled in parser, see CertInfo.Magic + + // Verify that extraData is set to the hash of attToBeSigned using the hash algorithm employed in "alg" + if (null == Alg || CBORType.Number != Alg.Type || false == CryptoUtils.algMap.ContainsKey(Alg.AsInt32())) throw new Fido2VerificationException("Invalid TPM attestation algorithm"); + if (!CryptoUtils.GetHasher(CryptoUtils.algMap[Alg.AsInt32()]).ComputeHash(Data).SequenceEqual(certInfo.ExtraData)) throw new Fido2VerificationException("Hash value mismatch extraData and attToBeSigned"); + + // Verify that attested contains a TPMS_CERTIFY_INFO structure, whose name field contains a valid Name for pubArea, as computed using the algorithm in the nameAlg field of pubArea + if (false == CryptoUtils.GetHasher(CryptoUtils.algMap[certInfo.Alg]).ComputeHash(pubArea.Raw).SequenceEqual(certInfo.AttestedName)) throw new Fido2VerificationException("Hash value mismatch attested and pubArea"); + + // If x5c is present, this indicates that the attestation type is not ECDAA + if (null != X5c && CBORType.Array == X5c.Type && 0 != X5c.Count) + { + if (null == X5c.Values || 0 == X5c.Values.Count || + CBORType.ByteString != X5c.Values.First().Type || + 0 == X5c.Values.First().GetByteString().Length) + throw new Fido2VerificationException("Malformed x5c in TPM attestation"); + + // Verify the sig is a valid signature over certInfo using the attestation public key in aikCert with the algorithm specified in alg. + var aikCert = new X509Certificate2(X5c.Values.First().GetByteString()); + + var coseKey = CryptoUtils.CoseKeyFromCertAndAlg(aikCert, Alg.AsInt32()); + + if (true != CryptoUtils.VerifySigWithCoseKey(certInfo.Raw, coseKey, Sig.GetByteString())) + throw new Fido2VerificationException("Bad signature in TPM with aikCert"); + + // Verify that aikCert meets the TPM attestation statement certificate requirements + // https://www.w3.org/TR/webauthn/#tpm-cert-requirements + // Version MUST be set to 3 + if (3 != aikCert.Version) + throw new Fido2VerificationException("aikCert must be V3"); + + // Subject field MUST be set to empty - they actually mean subject name + if (0 != aikCert.SubjectName.Name.Length) + throw new Fido2VerificationException("aikCert subject must be empty"); + + // The Subject Alternative Name extension MUST be set as defined in [TPMv2-EK-Profile] section 3.2.9. + // https://www.w3.org/TR/webauthn/#tpm-cert-requirements + var SAN = AuthDataHelper.SANFromAttnCertExts(aikCert.Extensions); + if (null == SAN || 0 == SAN.Length) + throw new Fido2VerificationException("SAN missing from TPM attestation certificate"); + + // From https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf + // "The issuer MUST include TPM manufacturer, TPM part number and TPM firmware version, using the directoryName + // form within the GeneralName structure. The ASN.1 encoding is specified in section 3.1.2 TPM Device + // Attributes. In accordance with RFC 5280[11], this extension MUST be critical if subject is empty + // and SHOULD be non-critical if subject is non-empty" + + // Best I can figure to do for now? + if (false == SAN.Contains("TPMManufacturer") || false == SAN.Contains("TPMModel") || + false == SAN.Contains("TPMVersion")) + throw new Fido2VerificationException("SAN missing TPMManufacturer, TPMModel, or TPMVersopm from TPM attestation certificate"); + + // The Extended Key Usage extension MUST contain the "joint-iso-itu-t(2) internationalorganizations(23) 133 tcg-kp(8) tcg-kp-AIKCertificate(3)" OID. + // OID is 2.23.133.8.3 + var EKU = AuthDataHelper.EKUFromAttnCertExts(aikCert.Extensions); + if (null == EKU || 0 != EKU.CompareTo("Attestation Identity Key Certificate (2.23.133.8.3)")) + throw new Fido2VerificationException("Invalid EKU on AIK certificate"); + + // The Basic Constraints extension MUST have the CA component set to false. + if (AuthDataHelper.IsAttnCertCACert(aikCert.Extensions)) + throw new Fido2VerificationException("aikCert Basic Constraints extension CA component must be false"); + + // If aikCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) verify that the value of this extension matches the aaguid in authenticatorData + var aaguid = AuthDataHelper.AaguidFromAttnCertExts(aikCert.Extensions); + if ((null != aaguid) && (!aaguid.SequenceEqual(Guid.Empty.ToByteArray())) && (!aaguid.SequenceEqual(AuthData.AttData.Aaguid.ToArray()))) throw new Fido2VerificationException("aaguid malformed"); + + // If successful, return attestation type AttCA and attestation trust path x5c. + return new AttestationFormatVerificationResult() + { + attnType = AttestationType.AttCa, + trustPath = X5c.Values + .Select(x => new X509Certificate2(x.GetByteString())) + .ToArray() + }; + } + // If ecdaaKeyId is present, then the attestation type is ECDAA + else if (null != EcdaaKeyId) + { + // Perform ECDAA-Verify on sig to verify that it is a valid signature over certInfo + // https://www.w3.org/TR/webauthn/#biblio-fidoecdaaalgorithm + throw new Fido2VerificationException("ECDAA support for TPM attestation is not yet implemented"); + // If successful, return attestation type ECDAA and the identifier of the ECDAA-Issuer public key ecdaaKeyId. + //attnType = AttestationType.ECDAA; + //trustPath = ecdaaKeyId; + } + else throw new Fido2VerificationException("Neither x5c nor ECDAA were found in the TPM attestation statement"); + + } + } +} diff --git a/fido2-net-lib/AuthDataHelper.cs b/fido2-net-lib/AuthDataHelper.cs index e3926997..a1a9ed25 100644 --- a/fido2-net-lib/AuthDataHelper.cs +++ b/fido2-net-lib/AuthDataHelper.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Collections.Generic; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using PeterO.Cbor; @@ -12,200 +11,6 @@ namespace Fido2NetLib /// public static class AuthDataHelper { - public static HashAlgorithm GetHasher(HashAlgorithmName hashName) - { - switch (hashName.Name) - { - case "SHA1": - return SHA1.Create(); - case "SHA256": - case "HS256" : - case "RS256" : - case "ES256" : - case "PS256" : - return SHA256.Create(); - case "SHA384": - case "HS384": - case "RS384": - case "ES384": - case "PS384": - return SHA384.Create(); - case "SHA512": - case "HS512": - case "RS512": - case "ES512": - case "PS512": - return SHA512.Create(); - default: - throw new ArgumentOutOfRangeException("hashName"); - } - } - - public static readonly Dictionary algMap = new Dictionary - { - {-65535, HashAlgorithmName.SHA1 }, - {-7, HashAlgorithmName.SHA256}, - {-35, HashAlgorithmName.SHA384 }, - {-36, HashAlgorithmName.SHA512 }, - {-37, HashAlgorithmName.SHA256 }, - {-38, HashAlgorithmName.SHA384 }, - {-39, HashAlgorithmName.SHA512 }, - {-257, HashAlgorithmName.SHA256 }, - {-258, HashAlgorithmName.SHA384 }, - {-259, HashAlgorithmName.SHA512 }, - {4, HashAlgorithmName.SHA1 }, - {11, HashAlgorithmName.SHA256 }, - {12, HashAlgorithmName.SHA384 }, - {13, HashAlgorithmName.SHA512 } - }; - public static readonly Dictionary CoseKeyTypeFromOid = new Dictionary - { - { "1.2.840.10045.2.1", 2 }, // ECC - { "1.2.840.113549.1.1.1", 3} // RSA - }; - public static readonly Dictionary CoseCurveToTpm = new Dictionary - { - { 1, TpmEccCurve.TPM_ECC_NIST_P256}, - { 2, TpmEccCurve.TPM_ECC_NIST_P384}, - { 3, TpmEccCurve.TPM_ECC_NIST_P521} - }; - public static CBORObject CoseKeyFromCertAndAlg(X509Certificate2 cert, Int32 alg) - { - var coseKey = CBORObject.NewMap(); - var kty = AuthDataHelper.CoseKeyTypeFromOid[cert.GetKeyAlgorithm()]; - coseKey.Add(1, kty); - coseKey.Add(3, alg); - if (3 == kty) - { - var keyParams = cert.GetRSAPublicKey().ExportParameters(false); - coseKey.Add(-1, keyParams.Modulus); - coseKey.Add(-2, keyParams.Exponent); - } - if (2 == kty) - { - var ecDsaPubKey = (ECDsaCng)cert.GetECDsaPublicKey(); - var keyParams = ecDsaPubKey.ExportParameters(false); - if (keyParams.Curve.Oid.FriendlyName.Equals(ECCurve.NamedCurves.nistP256.Oid.FriendlyName)) coseKey.Add(-1, 1); - if (keyParams.Curve.Oid.FriendlyName.Equals(ECCurve.NamedCurves.nistP384.Oid.FriendlyName)) coseKey.Add(-1, 2); - if (keyParams.Curve.Oid.FriendlyName.Equals(ECCurve.NamedCurves.nistP521.Oid.FriendlyName)) coseKey.Add(-1, 3); - coseKey.Add(-2, keyParams.Q.X); - coseKey.Add(-3, keyParams.Q.Y); - } - return coseKey; - } - public static bool VerifySigWithCoseKey(byte[] data, CBORObject coseKey, byte[] sig) - { - // Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData - var kty = coseKey[CBORObject.FromObject(1)].AsInt32(); - var alg = coseKey[CBORObject.FromObject(3)].AsInt32(); - var crv = 0; - if (1 == kty || 2 == kty) crv = coseKey[CBORObject.FromObject(-1)].AsInt32(); - switch (kty) // https://www.iana.org/assignments/cose/cose.xhtml#key-type - { - case 1: // OKP - { - switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms - { - case -8: - switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves - { - case 6: - return Chaos.NaCl.Ed25519.Verify(sig, GetHasher(HashAlgorithmName.SHA512).ComputeHash(data), coseKey[CBORObject.FromObject(-2)].GetByteString()); - default: - throw new ArgumentOutOfRangeException("crv"); - } - default: - throw new ArgumentOutOfRangeException("alg"); - } - } - case 2: // EC2 - { - var point = new ECPoint - { - X = coseKey[CBORObject.FromObject(-2)].GetByteString(), - Y = coseKey[CBORObject.FromObject(-3)].GetByteString() - }; - ECCurve curve; - switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms - { - case -7: - switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves - { - case 1: - case 8: - curve = ECCurve.NamedCurves.nistP256; - break; - default: - throw new ArgumentOutOfRangeException("crv"); - } - break; - case -35: - switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves - { - case 2: - curve = ECCurve.NamedCurves.nistP384; - break; - default: - throw new ArgumentOutOfRangeException("crv"); - } - break; - case -36: - switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves - { - case 3: - curve = ECCurve.NamedCurves.nistP521; - System.Diagnostics.Debug.WriteLine(BitConverter.ToString(sig).Replace("-", "")); - break; - default: - throw new ArgumentOutOfRangeException("crv"); - } - break; - default: - throw new ArgumentOutOfRangeException("alg"); - } - var cng = ECDsaCng.Create(new ECParameters - { - Q = point, - Curve = curve - }); - var ecsig = SigFromEcDsaSig(sig); - System.Diagnostics.Debug.WriteLine(BitConverter.ToString(ecsig).Replace("-", "")); - return cng.VerifyData(data, ecsig, algMap[alg]); - } - case 3: // RSA - { - RSACng rsa = new RSACng(); - rsa.ImportParameters( - new RSAParameters() - { - Modulus = coseKey[CBORObject.FromObject(-1)].GetByteString(), - Exponent = coseKey[CBORObject.FromObject(-2)].GetByteString() - } - ); - RSASignaturePadding padding; - switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms - { - - case -37: - case -38: - case -39: - padding = RSASignaturePadding.Pss; - break; - - case -65535: - case -257: - case -258: - case -259: - padding = RSASignaturePadding.Pkcs1; - break; - default: - throw new ArgumentOutOfRangeException("alg"); - } - return rsa.VerifyData(data, sig, algMap[alg], padding); - } - } - throw new Fido2VerificationException("Missing or unknown keytype"); - } public static byte[] AaguidFromAttnCertExts(X509ExtensionCollection exts) { byte[] aaguid = null; @@ -256,7 +61,7 @@ public static bool IsAttnCertCACert(X509ExtensionCollection exts) { if (ext.Oid.FriendlyName == "Basic Constraints") { - X509BasicConstraintsExtension baseExt = (X509BasicConstraintsExtension)ext; + var baseExt = (X509BasicConstraintsExtension)ext; return baseExt.CertificateAuthority; } } @@ -303,14 +108,6 @@ public static bool IsValidPackedAttnCertSubject(string attnCertSubj) "Authenticator Attestation" == dictSubject["OU"].ToString()); } - public static Memory U2FKeyFromCOSEKey(CBORObject COSEKey) - { - var x = COSEKey[CBORObject.FromObject(-2)].GetByteString(); - var y = COSEKey[CBORObject.FromObject(-3)].GetByteString(); - var publicKeyU2F = new byte[1] { 0x4 }; // uncompressed - return publicKeyU2F.Concat(x).Concat(y).ToArray(); - } - public static byte[] GetSizedByteArray(Memory ab, ref int offset, UInt16 len = 0) { if ((0 == len) && ((offset + 2) <= ab.Length)) @@ -326,230 +123,6 @@ public static byte[] GetSizedByteArray(Memory ab, ref int offset, UInt16 l } return result; } - public static byte[] GetASN1ObjectAtIndex(byte[] attExtBytes, int index) - { - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // This function returns an entry from the KeyDescription at index - if (null == attExtBytes || 0 == attExtBytes.Length || attExtBytes.Length > UInt16.MaxValue) throw new Fido2VerificationException("Invalid attExtBytes signature value"); - var offset = 0; - var derSequence = GetSizedByteArray(attExtBytes, ref offset, 1); - // expecting to start with 0x30 indicating SEQUENCE - if (null == derSequence || 0x30 != derSequence[0]) throw new Fido2VerificationException("attExtBytes signature not a valid DER sequence"); - // next is length of all the items in the sequence - var dataLen = GetSizedByteArray(attExtBytes, ref offset, 1); - if (null == dataLen) throw new Fido2VerificationException("attExtBytes signature has invalid length"); - // if data is more than 127 bytes, the length is encoded in long form - var longForm = (dataLen[0] > 0x7f); - var longLen = 0; - if (true == longForm) - { - var longLenByte = GetSizedByteArray(attExtBytes, ref offset, 1); - if (null == longLenByte) throw new Fido2VerificationException("attExtBytes signature has invalid long form length"); - longLen = longLenByte[0]; - longLen &= (1 << 7); - } - - // walk through each sequence entry until we get to the requested index - for (var i = 0; i < index; i++) - { - var derId = GetSizedByteArray(attExtBytes, ref offset, 1); - if (null == derId) throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); - var lenValue = GetSizedByteArray(attExtBytes, ref offset, 1); - if (null == lenValue) throw new Fido2VerificationException("attExtBytes lenValue invalid"); - if (0 < lenValue[0]) - { - var value = GetSizedByteArray(attExtBytes, ref offset, lenValue[0]); - if (null == value) throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); - } - } - // skip the identifier of the requested item - var asn1Id = GetSizedByteArray(attExtBytes, ref offset, 1); - if (null == asn1Id) throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); - // get length of requested item - var lenAsn1value = GetSizedByteArray(attExtBytes, ref offset, 1); - if (null == lenAsn1value) throw new Fido2VerificationException("lenAttestationChallenge version length invalid"); - // return byte array containing the requested item - return GetSizedByteArray(attExtBytes, ref offset, lenAsn1value[0]); - } - public static byte[] GetAttestationChallenge(byte[] attExtBytes) - { - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // attestationChallenge at index 4 - return GetASN1ObjectAtIndex(attExtBytes, 4); - } - public static byte[] GetSoftwareEnforcedAuthorizationList(byte[] attExtBytes) - { - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // softwareEnforced AuthorizationList at index 6 - return GetASN1ObjectAtIndex(attExtBytes, 6); - } - public static byte[] GetTeeEnforcedAuthorizationList(byte[] attExtBytes) - { - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // teeEnforced AuthorizationList at index 7 - return GetASN1ObjectAtIndex(attExtBytes, 7); - } - - public static bool FindAllApplicationsField(byte[] attExtBytes) - { - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // check both software and tee enforced AuthorizationList objects for presense of "allApplications" tag, number 600 - var software = GetSoftwareEnforcedAuthorizationList(attExtBytes); - var tee = GetTeeEnforcedAuthorizationList(attExtBytes); - var ignore = -1; - // allApplications is tag 600, and should not be found in either list - return (GetDERTagValue(software, 600, ref ignore) && GetDERTagValue(tee, 600, ref ignore)); - } - public static bool GetDERTagValue(byte[] authList, int tag, ref int result) - { - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // walk authorizationList sequence looking for an item with requested tag - // if tag is found, return true and set the result value to the found int value - // the only two items we are expecting to find are purpose and origin - // which are set of integer or integer, so int result is ok for now - // if entire list is walked and tag is not found, return false - for (int i = 0; i < authList.Length;) - { - // expecting to see first byte indicting the attribute is of class 2, and constructed value - // first two bits are the class, expecting to see 10 - // third bit is primative (0) or constructed (1) - if (false == ((authList[i] & 0xA0) == 0xA0)) throw new Fido2VerificationException("Expected class 2 constructed ASN.1 value"); - var foundTag = 0; - // if the tag value is below 0x1F (11111), the value is stored in the remaining 5 bits of the first byte - if (false == ((authList[i] & 0x1F) == 0x1F)) - { - foundTag = (authList[i] & ~0xA0); - i++; - } - // otherwise, if the tag value is 0x3FFF (11111111111111) or below - // the value is stored in the lower 7 bits of the second byte, and the lower 7 bits of the third byte - // this is signified by the high order bit set in the second byte, but not set in the third byte - else if (((authList[i + 1] & 0x80) == 0x80) && ((authList[i + 2] & 0x80) == 0x0)) - { - // take the lower 7 bits in the second byte, shift them left 7 positions - // then add the lower 7 bits of the third byte to get the tag value - // Welcome to Abstract Syntax Notation One - foundTag = ((authList[i + 1] & ~0x80) << 7) + (authList[i + 2]); - i += 3; - } - else throw new Fido2VerificationException("Expecting ASN.1 tag less than 0x3FFF"); - // if the tag we found is the one that we are looking for, get the value - if (tag == foundTag) - { - // 5 bytes will be remaining if this a set (0x31), advance to the integer - if (5 == authList[i] && 0x31 == authList[i + 1]) i += 2; - // for our purposes, we should see that there are 3 bytes remaining after this one - // the second byte should be 2 indicating the value is an integer - // and the third byte is the length in bytes, which again, for our purposes, should be one - if (3 == authList[i] && 2 == authList[i + 1] && 1 == authList[i + 2]) - { - // value is stored in the 4th byte, no need to go any further, we have our result - result = authList[i + 3]; - return true; - } - else throw new Fido2VerificationException("Unexpected byte sequence found fetching ASN.1 integer value"); - } - // if we didn't find the tag we were looking for, advance the index - // by the the size in bytes of the current tag plus one byte for the size - else if (i < authList.Length) i += authList[i] + 1; - } - // ran out of bytes without finding the tag we were looking for - return false; - } - - public static bool IsOriginGenerated(byte[] attExtBytes) - { - var tagValue = -1; - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // origin tag is 702 - var result = GetDERTagValue(GetTeeEnforcedAuthorizationList(attExtBytes), 702, ref tagValue); - return (0 == tagValue && true == result); - } - public static bool IsPurposeSign(byte[] attExtBytes) - { - var tagValue = -1; - // https://developer.android.com/training/articles/security-key-attestation#certificate_schema - // purpose tag is 1 - var result = GetDERTagValue(GetTeeEnforcedAuthorizationList(attExtBytes), 1, ref tagValue); - return (2 == tagValue && true == result); - } - public static byte[] GetEcDsaSigValue(byte[] ecDsaSig, ref int offset, bool longForm) - { - var derInt = GetSizedByteArray(ecDsaSig, ref offset, 1); - if (null == derInt || 0x02 != derInt[0]) throw new Fido2VerificationException("ECDsa signature coordinate sequence does not contain DER integer value"); // DER INTEGER - var lenByte = GetSizedByteArray(ecDsaSig, ref offset, 1); - if (null == lenByte) throw new Fido2VerificationException("ECDsa signature coordinate integer size invalid"); - var len = (UInt16) lenByte[0]; - if (false == longForm) - { - /* - * Ecdsa-Sig-Value ::= SEQUENCE { - * r INTEGER, - * s INTEGER } - * - * From: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer - * - * "Integer values are encoded into a TLV triplet that begins with a Tag value of 0x02. - * The Value field of the TLV triplet contains the encoded integer if it is positive, - * or its two's complement if it is negative. If the integer is positive but the high - * order bit is set to 1, a leading 0x00 is added to the content to indicate that the - * number is not negative." - * - */ - if (0x00 == ecDsaSig[offset] && ((ecDsaSig[offset + 1] & (1 << 7)) != 0)) - { - offset++; - len--; - } - } - return GetSizedByteArray(ecDsaSig, ref offset, len); - } - public static byte[] SigFromEcDsaSig(byte[] ecDsaSig) - { - // sanity check of input data - if (null == ecDsaSig || 0 == ecDsaSig.Length || ecDsaSig.Length > UInt16.MaxValue) throw new Fido2VerificationException("Invalid ECDsa signature value"); - // first byte should be DER sequence marker - var offset = 0; - var derSequence = GetSizedByteArray(ecDsaSig, ref offset, 1); - if (null == derSequence || 0x30 != derSequence[0]) throw new Fido2VerificationException("ECDsa signature not a valid DER sequence"); - // two forms of length, short form and long form - // short form, one byte, bit 8 not set, rest of the bits indicate data length - var dataLen = GetSizedByteArray(ecDsaSig, ref offset, 1); - if (null == dataLen) throw new Fido2VerificationException("ECDsa signature has invalid length"); - // long form, first byte, bit 8 is set, rest of bits indicate the length of the data length - // so if bit 8 is on... - var longForm = (0 != (dataLen[0] & (1 << 7))); - if (true == longForm) - { - // rest of bits indicate the length of the data length - var longLen = (dataLen[0] & ~(1 << 7)); - // sanity check of input data - if (UInt16.MinValue > longLen || UInt16.MaxValue < longLen) throw new Fido2VerificationException("ECDsa signature has invalid long form length"); - // total length of remaining data - var longLenByte = GetSizedByteArray(ecDsaSig, ref offset, (UInt16) longLen); - if (null == longLenByte) throw new Fido2VerificationException("ECDsa signature has invalid long form length"); - longLen = (UInt16)longLenByte[0]; - // sanity check the length - if (ecDsaSig.Length != (offset + longLen)) throw new Fido2VerificationException("ECDsa signature has invalid long form length"); - } - - // Get R value - var r = GetEcDsaSigValue(ecDsaSig, ref offset, longForm); - if (null == r) throw new Fido2VerificationException("ECDsa signature R integer value invalid"); - - // Get S value - var s = GetEcDsaSigValue(ecDsaSig, ref offset, longForm); - if (null == s) throw new Fido2VerificationException("ECDsa signature S integer value invalid"); - - // make sure we are at the end - if (ecDsaSig.Length != offset) throw new Fido2VerificationException("ECDsa signature has bytes leftover after parsing R and S values"); - - // combine the coordinates and return the raw sign - var sig = new byte[s.Length + r.Length]; - Buffer.BlockCopy(r, 0, sig, 0, r.Length); - Buffer.BlockCopy(s, 0, sig, r.Length, s.Length); - return sig; - } } // https://w3c.github.io/webauthn/#authenticator-data public class AuthenticatorData diff --git a/fido2-net-lib/AuthenticatorAssertionResponse.cs b/fido2-net-lib/AuthenticatorAssertionResponse.cs index a8dd8aec..7fe57a17 100644 --- a/fido2-net-lib/AuthenticatorAssertionResponse.cs +++ b/fido2-net-lib/AuthenticatorAssertionResponse.cs @@ -136,14 +136,12 @@ public async Task VerifyAsync(AssertionOptions opti if (null == storedPublicKey || 0 == storedPublicKey.Length) throw new Fido2VerificationException("Stored public key is null or empty"); var coseKey = PeterO.Cbor.CBORObject.DecodeFromBytes(storedPublicKey); - if (true != AuthDataHelper.VerifySigWithCoseKey(data, coseKey, Signature)) throw new Fido2VerificationException("Signature did not match"); + if (true != CryptoUtils.VerifySigWithCoseKey(data, coseKey, Signature)) throw new Fido2VerificationException("Signature did not match"); // 17. var counter = BitConverter.ToUInt32(authData.SignCount.ToArray().Reverse().ToArray(), 0); if (counter > 0 && counter <= storedSignatureCounter) - { throw new Fido2VerificationException("SignatureCounter was not greater than stored SignatureCounter"); - } return new AssertionVerificationResult() { diff --git a/fido2-net-lib/AuthenticatorAttestationResponse.cs b/fido2-net-lib/AuthenticatorAttestationResponse.cs index 7c3e0055..e2f5ffc6 100644 --- a/fido2-net-lib/AuthenticatorAttestationResponse.cs +++ b/fido2-net-lib/AuthenticatorAttestationResponse.cs @@ -1,21 +1,20 @@ using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using Fido2NetLib.Objects; -using Microsoft.IdentityModel.Tokens; -using Newtonsoft.Json.Linq; +using Fido2NetLib.AttestationFormat; using PeterO.Cbor; namespace Fido2NetLib { /// - /// The AuthenticatorAttestationResponse interface represents the authenticator's response to a client’s request for the creation of a new public key credential. - /// It contains information about the new credential that can be used to identify it for later use, and metadata that can be used by the Relying Party to assess the characteristics of the credential during registration. + /// The AuthenticatorAttestationResponse interface represents the authenticator's response + /// to a client’s request for the creation of a new public key credential. + /// It contains information about the new credential that can be used to identify it for later use, + /// and metadata that can be used by the Relying Party to assess the characteristics of the credential during registration. /// public class AuthenticatorAttestationResponse : AuthenticatorResponse { @@ -28,9 +27,12 @@ private AuthenticatorAttestationResponse(byte[] clientDataJson) : base(clientDat public static AuthenticatorAttestationResponse Parse(AuthenticatorAttestationRawResponse rawResponse) { - if (null == rawResponse || null == rawResponse.Response) throw new Fido2VerificationException("Expected rawResponse, got null"); + if (null == rawResponse || null == rawResponse.Response) + throw new Fido2VerificationException("Expected rawResponse, got null"); - if (null == rawResponse.Response.AttestationObject || 0 == rawResponse.Response.AttestationObject.Length) throw new Fido2VerificationException("Missing AttestationObject"); + if (null == rawResponse.Response.AttestationObject || 0 == rawResponse.Response.AttestationObject.Length) + throw new Fido2VerificationException("Missing AttestationObject"); + CBORObject cborAttestation = null; try { @@ -49,7 +51,7 @@ public static AuthenticatorAttestationResponse Parse(AuthenticatorAttestationRaw CBORType.ByteString != cborAttestation["authData"].Type ) throw new Fido2VerificationException("Malformed AttestationObject"); - AuthenticatorAttestationResponse response = new AuthenticatorAttestationResponse(rawResponse.Response.ClientDataJson) + var response = new AuthenticatorAttestationResponse(rawResponse.Response.ClientDataJson) { Raw = rawResponse, AttestationObject = new ParsedAttestationObject() @@ -64,472 +66,110 @@ public static AuthenticatorAttestationResponse Parse(AuthenticatorAttestationRaw public async Task VerifyAsync(CredentialCreateOptions originalOptions, string expectedOrigin, IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser, IMetadataService metadataService, byte[] requestTokenBindingId) { - AttestationType attnType; - X509Certificate2[] trustPath = null; + AttestationFormatVerificationResult attFmtVerificationResult; BaseVerify(expectedOrigin, originalOptions.Challenge, requestTokenBindingId); // verify challenge is same as we expected // verify origin // done in baseclass - if (Type != "webauthn.create") throw new Fido2VerificationException("AttestationResponse is not type webauthn.create"); + if (Type != "webauthn.create") + throw new Fido2VerificationException("AttestationResponse is not type webauthn.create"); + + if (Raw.Id == null || Raw.Id.Length == 0) + throw new Fido2VerificationException("AttestationResponse is missing Id"); - if (Raw.Id == null || Raw.Id.Length == 0) throw new Fido2VerificationException("AttestationResponse is missing Id"); + if (Raw.Type != "public-key") + throw new Fido2VerificationException("AttestationResponse is missing type with value 'public-key'"); - if (Raw.Type != "public-key") throw new Fido2VerificationException("AttestationResponse is missing type with value 'public-key'"); + if (null == AttestationObject.AuthData || 0 == AttestationObject.AuthData.Length) + throw new Fido2VerificationException("Missing or malformed authData"); - if (null == AttestationObject.AuthData || 0 == AttestationObject.AuthData.Length) throw new Fido2VerificationException("Missing or malformed authData"); - AuthenticatorData authData = new AuthenticatorData(AttestationObject.AuthData); + var authData = new AuthenticatorData(AttestationObject.AuthData); // 6 - //todo: Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over which the assertion was obtained.If Token Binding was used on that TLS connection, also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection. - // This id done in BaseVerify. - // todo: test that implmentation + //todo: Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection + // over which the assertion was obtained.If Token Binding was used on that TLS connection, + // also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection. + // This is done in BaseVerify. + // TODO: test that implmentation // 7 // Compute the hash of response.clientDataJSON using SHA - 256. - byte[] hashedClientDataJson; - byte[] hashedRpId; - using (var sha = SHA256.Create()) + byte[] clientDataHash, rpIdHash; + using (var sha = CryptoUtils.GetHasher(HashAlgorithmName.SHA256)) { - hashedClientDataJson = sha.ComputeHash(Raw.Response.ClientDataJson); - hashedRpId = sha.ComputeHash(Encoding.UTF8.GetBytes(originalOptions.Rp.Id)); + clientDataHash = sha.ComputeHash(Raw.Response.ClientDataJson); + rpIdHash = sha.ComputeHash(Encoding.UTF8.GetBytes(originalOptions.Rp.Id)); } // 9 // Verify that the RP ID hash in authData is indeed the SHA - 256 hash of the RP ID expected by the RP. - if (false == authData.RpIdHash.SequenceEqual(hashedRpId)) throw new Fido2VerificationException("Hash mismatch RPID"); + if (false == authData.RpIdHash.SequenceEqual(rpIdHash)) + throw new Fido2VerificationException("Hash mismatch RPID"); // 10 // Verify that the User Present bit of the flags in authData is set. - if (false == authData.UserPresent) throw new Fido2VerificationException("User Present flag not set in authenticator data"); + if (false == authData.UserPresent) + throw new Fido2VerificationException("User Present flag not set in authenticator data"); // 11 // If user verification is required for this registration, verify that the User Verified bit of the flags in authData is set. - var userVerified = authData.UserVerified; + // see authData.UserVerified // 12 // Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected // todo: Implement sort of like this: ClientExtensions.Keys.Any(x => options.extensions.contains(x); - // A COSEAlgorithmIdentifier containing the identifier of the algorithm used to generate the attestation signature - var alg = AttestationObject.AttStmt["alg"]; - // A byte string containing the attestation signature - var sig = AttestationObject.AttStmt["sig"]; - // The elements of this array contain attestnCert and its certificate chain, each encoded in X.509 format - var x5c = AttestationObject.AttStmt["x5c"]; - // The identifier of the ECDAA-Issuer public key - var ecdaaKeyId = AttestationObject.AttStmt["ecdaaKeyId"]; - - if (false == authData.AttestedCredentialDataPresent) throw new Fido2VerificationException("Attestation flag not set on attestation data"); - var credentialId = authData.AttData.CredentialID; - var credentialPublicKeyBytes = authData.AttData.CredentialPublicKey.ToArray(); - CBORObject credentialPublicKey = null; - var coseKty = 0; - var coseAlg = 0; - try - { - credentialPublicKey = CBORObject.DecodeFromBytes(authData.AttData.CredentialPublicKey); - coseKty = credentialPublicKey[CBORObject.FromObject(1)].AsInt32(); - coseAlg = credentialPublicKey[CBORObject.FromObject(3)].AsInt32(); - } - catch (CBORException) - { - throw new Fido2VerificationException("Malformed credentialPublicKey"); - } - byte[] data = new byte[AttestationObject.AuthData.Length + hashedClientDataJson.Length]; - Buffer.BlockCopy(AttestationObject.AuthData, 0, data, 0, AttestationObject.AuthData.Length); - Buffer.BlockCopy(hashedClientDataJson, 0, data, AttestationObject.AuthData.Length, hashedClientDataJson.Length); + if (false == authData.AttestedCredentialDataPresent) + throw new Fido2VerificationException("Attestation flag not set on attestation data"); + // 13 - // Determine the attestation statement format by performing a USASCII case-sensitive match on fmt against the set of supported WebAuthn Attestation Statement Format Identifier values. The up-to-date list of registered WebAuthn Attestation Statement Format Identifier values is maintained in the in the IANA registry of the same name [WebAuthn-Registries]. + // Determine the attestation statement format by performing a US ASCII case-sensitive match on fmt against the set of supported WebAuthn Attestation Statement Format Identifier values. The up-to-date list of registered WebAuthn Attestation Statement Format Identifier values is maintained in the in the IANA registry of the same name [WebAuthn-Registries]. // https://www.w3.org/TR/webauthn/#defined-attestation-formats + AttestationFormat.AttestationFormat verifier; switch (AttestationObject.Fmt) { // 14 // validate the attStmt - case "none": - { // https://www.w3.org/TR/webauthn/#none-attestation - - if (0 != AttestationObject.AttStmt.Keys.Count && 0 != AttestationObject.AttStmt.Values.Count) throw new Fido2VerificationException("Attestation format none should have no attestation statement"); - attnType = AttestationType.None; - trustPath = null; - } + verifier = new None(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataHash); break; case "tpm": - { // https://www.w3.org/TR/webauthn/#tpm-attestation - - if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid TPM attestation signature"); - if ("2.0" != AttestationObject.AttStmt["ver"].AsString()) throw new Fido2VerificationException("FIDO2 only supports TPM 2.0"); - - // Verify that the public key specified by the parameters and unique fields of pubArea is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData - PubArea pubArea = null; - if (null != AttestationObject.AttStmt["pubArea"] && CBORType.ByteString == AttestationObject.AttStmt["pubArea"].Type && 0 != AttestationObject.AttStmt["pubArea"].GetByteString().Length) - { - pubArea = new PubArea(AttestationObject.AttStmt["pubArea"].GetByteString()); - } - if (null == pubArea || null == pubArea.Unique || 0 == pubArea.Unique.Length) throw new Fido2VerificationException("Missing or malformed pubArea"); - if (3 == coseKty) // RSA - { - var coseMod = credentialPublicKey[CBORObject.FromObject(-1)].GetByteString(); // modulus - var coseExp = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); // exponent - - if (!coseMod.ToArray().SequenceEqual(pubArea.Unique.ToArray())) throw new Fido2VerificationException("Public key mismatch between pubArea and credentialPublicKey"); - if ((coseExp[0] + (coseExp[1] << 8) + (coseExp[2] << 16)) != pubArea.Exponent) throw new Fido2VerificationException("Public key exponent mismatch between pubArea and credentialPublicKey"); - } - else if (2 == coseKty) // ECC - { - var curve = credentialPublicKey[CBORObject.FromObject(-1)].AsInt32(); - var X = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); - var Y = credentialPublicKey[CBORObject.FromObject(-3)].GetByteString(); - - if (pubArea.EccCurve != AuthDataHelper.CoseCurveToTpm[curve]) throw new Fido2VerificationException("Curve mismatch between pubArea and credentialPublicKey"); - if (!pubArea.ECPoint.X.SequenceEqual(X)) throw new Fido2VerificationException("X-coordinate mismatch between pubArea and credentialPublicKey"); - if (!pubArea.ECPoint.Y.SequenceEqual(Y)) throw new Fido2VerificationException("Y-coordinate mismatch between pubArea and credentialPublicKey"); - } - // Concatenate authenticatorData and clientDataHash to form attToBeSigned. - // see data variable - - // Validate that certInfo is valid - CertInfo certInfo = null; - if (null != AttestationObject.AttStmt["certInfo"] && CBORType.ByteString == AttestationObject.AttStmt["certInfo"].Type && 0 != AttestationObject.AttStmt["certInfo"].GetByteString().Length) - { - certInfo = new CertInfo(AttestationObject.AttStmt["certInfo"].GetByteString()); - } - if (null == certInfo || null == certInfo.ExtraData || 0 == certInfo.ExtraData.Length) throw new Fido2VerificationException("CertInfo invalid parsing TPM format attStmt"); - // Verify that magic is set to TPM_GENERATED_VALUE and type is set to TPM_ST_ATTEST_CERTIFY - // handled in parser, see certInfo.Magic - - // Verify that extraData is set to the hash of attToBeSigned using the hash algorithm employed in "alg" - if (null == alg || CBORType.Number != alg.Type || false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid TPM attestation algorithm"); - if (!AuthDataHelper.GetHasher(AuthDataHelper.algMap[alg.AsInt32()]).ComputeHash(data).SequenceEqual(certInfo.ExtraData)) throw new Fido2VerificationException("Hash value mismatch extraData and attToBeSigned"); - - // Verify that attested contains a TPMS_CERTIFY_INFO structure, whose name field contains a valid Name for pubArea, as computed using the algorithm in the nameAlg field of pubArea - if (false == AuthDataHelper.GetHasher(AuthDataHelper.algMap[(int)certInfo.Alg]).ComputeHash(pubArea.Raw).SequenceEqual(certInfo.AttestedName)) throw new Fido2VerificationException("Hash value mismatch attested and pubArea"); - - // If x5c is present, this indicates that the attestation type is not ECDAA - if (null != x5c && CBORType.Array == x5c.Type && 0 != x5c.Count) - { - if (null == x5c.Values || 0 == x5c.Values.Count || CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in TPM attestation"); - - // Verify the sig is a valid signature over certInfo using the attestation public key in aikCert with the algorithm specified in alg. - var aikCert = new X509Certificate2(x5c.Values.First().GetByteString()); - - CBORObject coseKey = AuthDataHelper.CoseKeyFromCertAndAlg(aikCert, alg.AsInt32()); - - if (true != AuthDataHelper.VerifySigWithCoseKey(certInfo.Raw, coseKey, sig.GetByteString())) throw new Fido2VerificationException("Bad signature in TPM with aikCert"); - - // Verify that aikCert meets the TPM attestation statement certificate requirements - // https://www.w3.org/TR/webauthn/#tpm-cert-requirements - // Version MUST be set to 3 - if (3 != aikCert.Version) throw new Fido2VerificationException("aikCert must be V3"); - - // Subject field MUST be set to empty - they actually mean subject name - if (0 != aikCert.SubjectName.Name.Length) throw new Fido2VerificationException("aikCert subject must be empty"); - - // The Subject Alternative Name extension MUST be set as defined in [TPMv2-EK-Profile] section 3.2.9. - // https://www.w3.org/TR/webauthn/#tpm-cert-requirements - var SAN = AuthDataHelper.SANFromAttnCertExts(aikCert.Extensions); - if (null == SAN || 0 == SAN.Length) throw new Fido2VerificationException("SAN missing from TPM attestation certificate"); - // From https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf - // The issuer MUST include TPM manufacturer, TPM part number and TPM firmware version, using the directoryNameform within the GeneralName structure. The ASN.1 encoding is specified in section 3.1.2 TPM Device Attributes. In accordance with RFC 5280[11], this extension MUST be critical if subject is empty and SHOULD be non-critical if subject is non-empty.  - // Best I can figure to do for now? - if (false == SAN.Contains("TPMManufacturer") || false == SAN.Contains("TPMModel") || false == SAN.Contains("TPMVersion")) throw new Fido2VerificationException("SAN missing TPMManufacturer, TPMModel, or TPMVersopm from TPM attestation certificate"); - // The Extended Key Usage extension MUST contain the "joint-iso-itu-t(2) internationalorganizations(23) 133 tcg-kp(8) tcg-kp-AIKCertificate(3)" OID. - // OID is 2.23.133.8.3 - var EKU = AuthDataHelper.EKUFromAttnCertExts(aikCert.Extensions); - if (null == EKU || 0 != EKU.CompareTo("Attestation Identity Key Certificate (2.23.133.8.3)")) throw new Fido2VerificationException("Invalid EKU on AIK certificate"); - - // The Basic Constraints extension MUST have the CA component set to false. - if (AuthDataHelper.IsAttnCertCACert(aikCert.Extensions)) throw new Fido2VerificationException("aikCert Basic Constraints extension CA component must be false"); - - // If aikCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) verify that the value of this extension matches the aaguid in authenticatorData - var aaguid = AuthDataHelper.AaguidFromAttnCertExts(aikCert.Extensions); - if ((null != aaguid) && (!aaguid.SequenceEqual(Guid.Empty.ToByteArray())) && (!aaguid.SequenceEqual(authData.AttData.Aaguid.ToArray()))) throw new Fido2VerificationException("aaguid malformed"); - - // If successful, return attestation type AttCA and attestation trust path x5c. - attnType = AttestationType.AttCa; - trustPath = x5c.Values - .Select(x => new X509Certificate2(x.GetByteString())) - .ToArray(); - } - // If ecdaaKeyId is present, then the attestation type is ECDAA - else if (null != ecdaaKeyId) - { - // Perform ECDAA-Verify on sig to verify that it is a valid signature over certInfo - // https://www.w3.org/TR/webauthn/#biblio-fidoecdaaalgorithm - throw new Fido2VerificationException("ECDAA support for TPM attestation is not yet implemented"); - // If successful, return attestation type ECDAA and the identifier of the ECDAA-Issuer public key ecdaaKeyId. - //attnType = AttestationType.ECDAA; - //trustPath = ecdaaKeyId; - } - else throw new Fido2VerificationException("Neither x5c nor ECDAA were found in the TPM attestation statement"); - } + verifier = new Tpm(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataHash); break; case "android-key": - { // https://www.w3.org/TR/webauthn/#android-key-attestation - - // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields - if (0 == AttestationObject.AttStmt.Keys.Count || 0 == AttestationObject.AttStmt.Values.Count) throw new Fido2VerificationException("Attestation format packed must have attestation statement"); - if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid packed attestation signature"); - // 2a. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash - // using the attestation public key in attestnCert with the algorithm specified in alg - if (null == x5c && CBORType.Array != x5c.Type && 0 == x5c.Count) throw new Fido2VerificationException("Malformed x5c in android-key attestation"); - if (null == x5c.Values || 0 == x5c.Values.Count || CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in android-key attestation"); - X509Certificate2 androidKeyCert = null; - ECDsaCng androidKeyPubKey = null; - try - { - androidKeyCert = new X509Certificate2(x5c.Values.First().GetByteString()); - androidKeyPubKey = (ECDsaCng)androidKeyCert.GetECDsaPublicKey(); // attestation public key - } - catch (Exception ex) - { - throw new Fido2VerificationException("Failed to extract public key from android key" + ex.Message); - } - if (null == alg || CBORType.Number != alg.Type || false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid attestation algorithm"); - if (true != androidKeyPubKey.VerifyData(data, AuthDataHelper.SigFromEcDsaSig(sig.GetByteString()), AuthDataHelper.algMap[alg.AsInt32()])) throw new Fido2VerificationException("Invalid android key signature"); - var cng = ECDsaCng.Create(new ECParameters - { - Curve = ECCurve.NamedCurves.nistP256, - Q = new ECPoint - { - X = credentialPublicKey[CBORObject.FromObject(-2)].GetByteString(), - Y = credentialPublicKey[CBORObject.FromObject(-3)].GetByteString() - } - }); - // Verify that the public key in the first certificate in in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData. - if (true != cng.VerifyData(data, AuthDataHelper.SigFromEcDsaSig(sig.GetByteString()), AuthDataHelper.algMap[alg.AsInt32()])) throw new Fido2VerificationException("Invalid android key signature"); - - // Verify that in the attestation certificate extension data: - var attExtBytes = AuthDataHelper.AttestationExtensionBytes(androidKeyCert.Extensions); - - // 1. The value of the attestationChallenge field is identical to clientDataHash. - var attestationChallenge = AuthDataHelper.GetAttestationChallenge(attExtBytes); - if (false == hashedClientDataJson.SequenceEqual(attestationChallenge)) throw new Fido2VerificationException("Mismatched between attestationChallenge and hashedClientDataJson verifying android key attestation certificate extension"); - - // 2. The AuthorizationList.allApplications field is not present, since PublicKeyCredential MUST be bound to the RP ID. - if (true == AuthDataHelper.FindAllApplicationsField(attExtBytes)) throw new Fido2VerificationException("Found all applications field in android key attestation certificate extension"); - - // 3. The value in the AuthorizationList.origin field is equal to KM_ORIGIN_GENERATED ( which == 0). - if (false == AuthDataHelper.IsOriginGenerated(attExtBytes)) throw new Fido2VerificationException("Found origin field not set to KM_ORIGIN_GENERATED in android key attestation certificate extension"); - - // 4. The value in the AuthorizationList.purpose field is equal to KM_PURPOSE_SIGN (which == 2). - if (false == AuthDataHelper.IsPurposeSign(attExtBytes)) throw new Fido2VerificationException("Found purpose field not set to KM_PURPOSE_SIGN in android key attestation certificate extension"); - - attnType = AttestationType.Basic; - trustPath = x5c.Values - .Select(x => new X509Certificate2(x.GetByteString())) - .ToArray(); - } + verifier = new AndroidKey(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataHash); break; case "android-safetynet": - { // https://www.w3.org/TR/webauthn/#android-safetynet-attestation - - // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields - if ((CBORType.TextString != AttestationObject.AttStmt["ver"].Type) || (0 == AttestationObject.AttStmt["ver"].AsString().Length)) throw new Fido2VerificationException("Invalid version in SafetyNet data"); - - // Verify that response is a valid SafetyNet response of version ver - var ver = AttestationObject.AttStmt["ver"].AsString(); - - if ((CBORType.ByteString != AttestationObject.AttStmt["response"].Type) || (0 == AttestationObject.AttStmt["response"].GetByteString().Length)) throw new Fido2VerificationException("Invalid response in SafetyNet data"); - var response = AttestationObject.AttStmt["response"].GetByteString(); - var signedAttestationStatement = Encoding.UTF8.GetString(response); - var jwtToken = new JwtSecurityToken(signedAttestationStatement); - X509SecurityKey[] keys = (jwtToken.Header["x5c"] as JArray) - .Values() - .Select(x => new X509SecurityKey( - new X509Certificate2(Convert.FromBase64String(x)))) - .ToArray(); - if ((null == keys) || (0 == keys.Count())) throw new Fido2VerificationException("SafetyNet attestation missing x5c"); - var validationParameters = new TokenValidationParameters - { - ValidateIssuer = false, - ValidateAudience = false, - ValidateLifetime = false, - ValidateIssuerSigningKey = true, - IssuerSigningKeys = keys - }; - - var tokenHandler = new JwtSecurityTokenHandler(); - SecurityToken validatedToken; - - tokenHandler.ValidateToken( - signedAttestationStatement, - validationParameters, - out validatedToken); - - if (false == (validatedToken.SigningKey is X509SecurityKey)) throw new Fido2VerificationException("Safetynet signing key invalid"); - - var nonce = ""; - var payload = false; - foreach (var claim in jwtToken.Claims) - { - if (("nonce" == claim.Type) && ("http://www.w3.org/2001/XMLSchema#string" == claim.ValueType) && (0 != claim.Value.Length)) nonce = claim.Value; - if (("ctsProfileMatch" == claim.Type) && ("http://www.w3.org/2001/XMLSchema#boolean" == claim.ValueType)) - { - payload = bool.Parse(claim.Value); - } - if (("timestampMs" == claim.Type) && ("http://www.w3.org/2001/XMLSchema#integer64" == claim.ValueType)) - { - DateTime dt = DateTimeHelper.UnixEpoch.AddMilliseconds(double.Parse(claim.Value)); - if ((DateTime.UtcNow < dt) || (DateTime.UtcNow.AddMinutes(-1) > dt)) throw new Fido2VerificationException("Android SafetyNet timestampMs must be between one minute ago and now"); - } - } - - // Verify that the nonce in the response is identical to the SHA-256 hash of the concatenation of authenticatorData and clientDataHash - if ("" == nonce) throw new Fido2VerificationException("Nonce value not found in Android SafetyNet attestation"); - if (!AuthDataHelper.GetHasher(HashAlgorithmName.SHA256).ComputeHash(data).SequenceEqual(Convert.FromBase64String(nonce))) throw new Fido2VerificationException("Android SafetyNet hash value mismatch"); - - // Verify that the attestation certificate is issued to the hostname "attest.android.com" - if (false == ("attest.android.com").Equals((validatedToken.SigningKey as X509SecurityKey).Certificate.GetNameInfo(X509NameType.DnsName, false))) throw new Fido2VerificationException("Safetynet DnsName is not attest.android.com"); - - // Verify that the ctsProfileMatch attribute in the payload of response is true - if (true != payload) throw new Fido2VerificationException("Android SafetyNet ctsProfileMatch must be true"); - - attnType = AttestationType.Basic; - trustPath = (jwtToken.Header["x5c"] as JArray) - .Values() - .Select(x => new X509Certificate2(Convert.FromBase64String(x))) - .ToArray(); - } + verifier = new AndroidSafetyNet(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataHash); break; case "fido-u2f": - { // https://www.w3.org/TR/webauthn/#fido-u2f-attestation - - // verify that aaguid is 16 empty bytes (note: required by fido2 conformance testing, could not find this in spec?) - if (false == authData.AttData.Aaguid.SequenceEqual(Guid.Empty.ToByteArray())) throw new Fido2VerificationException("Aaguid was not empty parsing fido-u2f atttestation statement"); - - // 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. - if (null == x5c || CBORType.Array != x5c.Type || x5c.Count != 1) throw new Fido2VerificationException("Malformed x5c in fido - u2f attestation"); - - // 2a. the attestation certificate attestnCert MUST be the first element in the array - if (null == x5c.Values || 0 == x5c.Values.Count || CBORType.ByteString != x5c.Values.First().Type || 0 == x5c.Values.First().GetByteString().Length) throw new Fido2VerificationException("Malformed x5c in fido-u2f attestation"); - var cert = new X509Certificate2(x5c.Values.First().GetByteString()); - - // 2b. If certificate public key is not an Elliptic Curve (EC) public key over the P-256 curve, terminate this algorithm and return an appropriate error - var pubKey = (ECDsaCng)cert.GetECDsaPublicKey(); - if (CngAlgorithm.ECDsaP256 != pubKey.Key.Algorithm) throw new Fido2VerificationException("attestation certificate public key is not an Elliptic Curve (EC) public key over the P-256 curve"); - - // 3. Extract the claimed rpIdHash from authenticatorData, and the claimed credentialId and credentialPublicKey from authenticatorData - // done above - - // 4. Convert the COSE_KEY formatted credentialPublicKey (see Section 7 of [RFC8152]) to CTAP1/U2F public Key format - var publicKeyU2F = AuthDataHelper.U2FKeyFromCOSEKey(credentialPublicKey); - - // 5. Let verificationData be the concatenation of (0x00 || rpIdHash || clientDataHash || credentialId || publicKeyU2F) - var verificationData = new byte[1] { 0x00 }; - verificationData = verificationData.Concat(hashedRpId).Concat(hashedClientDataJson).Concat(credentialId).Concat(publicKeyU2F.ToArray()).ToArray(); - - // 6. Verify the sig using verificationData and certificate public key - if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); - var ecsig = AuthDataHelper.SigFromEcDsaSig(sig.GetByteString()); - if (null == ecsig) throw new Fido2VerificationException("Failed to decode fido-u2f attestation signature from ASN.1 encoded form"); - if (true != pubKey.VerifyData(verificationData, ecsig, AuthDataHelper.algMap[coseAlg])) throw new Fido2VerificationException("Invalid fido-u2f attestation signature"); - attnType = AttestationType.Basic; - trustPath = x5c.Values - .Select(x => new X509Certificate2(x.GetByteString())) - .ToArray(); - } + verifier = new FidoU2f(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataHash); break; case "packed": - { // https://www.w3.org/TR/webauthn/#packed-attestation - - // Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. - if (0 == AttestationObject.AttStmt.Keys.Count || 0 == AttestationObject.AttStmt.Values.Count) throw new Fido2VerificationException("Attestation format packed must have attestation statement"); - if (null == sig || CBORType.ByteString != sig.Type || 0 == sig.GetByteString().Length) throw new Fido2VerificationException("Invalid packed attestation signature"); - if (null == alg || CBORType.Number != alg.Type) throw new Fido2VerificationException("Invalid packed attestation algorithm"); - - // If x5c is present, this indicates that the attestation type is not ECDAA - if (null != x5c) - { - if (CBORType.Array != x5c.Type || 0 == x5c.Count || null != ecdaaKeyId) throw new Fido2VerificationException("Malformed x5c array in packed attestation statement"); - IEnumerator enumerator = x5c.Values.GetEnumerator(); - while (enumerator.MoveNext()) - { - if (null == enumerator || null == enumerator.Current || CBORType.ByteString != enumerator.Current.Type || 0 == enumerator.Current.GetByteString().Length) throw new Fido2VerificationException("Malformed x5c cert found in packed attestation statement"); - var x5ccert = new X509Certificate2(enumerator.Current.GetByteString()); - if (DateTime.UtcNow < x5ccert.NotBefore || DateTime.UtcNow > x5ccert.NotAfter) throw new Fido2VerificationException("Packed signing certificate expired or not yet valid"); - } - - // The attestation certificate attestnCert MUST be the first element in the array. - var attestnCert = new X509Certificate2(x5c.Values.First().GetByteString()); - - // 2a. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash - // using the attestation public key in attestnCert with the algorithm specified in alg - var packedPubKey = (ECDsaCng)attestnCert.GetECDsaPublicKey(); // attestation public key - if (false == AuthDataHelper.algMap.ContainsKey(alg.AsInt32())) throw new Fido2VerificationException("Invalid attestation algorithm"); - - var coseKey = AuthDataHelper.CoseKeyFromCertAndAlg(attestnCert, alg.AsInt32()); - - if (true != AuthDataHelper.VerifySigWithCoseKey(data, coseKey, sig.GetByteString())) throw new Fido2VerificationException("Invalid full packed signature"); - - // Verify that attestnCert meets the requirements in https://www.w3.org/TR/webauthn/#packed-attestation-cert-requirements - // 2b. Version MUST be set to 3 - if (3 != attestnCert.Version) throw new Fido2VerificationException("Packed x5c attestation certificate not V3"); - - // Subject field MUST contain C, O, OU, CN - // OU must match "Authenticator Attestation" - if (true != AuthDataHelper.IsValidPackedAttnCertSubject(attestnCert.Subject)) throw new Fido2VerificationException("Invalid attestation cert subject"); - - // 2c. If the related attestation root certificate is used for multiple authenticator models, - // the Extension OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) MUST be present, containing the AAGUID as a 16-byte OCTET STRING - // verify that the value of this extension matches the aaguid in authenticatorData - var aaguid = AuthDataHelper.AaguidFromAttnCertExts(attestnCert.Extensions); - if (aaguid != null && !aaguid.SequenceEqual(authData.AttData.Aaguid.ToArray())) throw new Fido2VerificationException("aaguid present in packed attestation but does not match aaguid from authData"); - - // 2d. The Basic Constraints extension MUST have the CA component set to false - if (AuthDataHelper.IsAttnCertCACert(attestnCert.Extensions)) throw new Fido2VerificationException("Attestion certificate has CA cert flag present"); - - // id-fido-u2f-ce-transports - var u2ftransports = AuthDataHelper.U2FTransportsFromAttnCert(attestnCert.Extensions); - attnType = AttestationType.Basic; - trustPath = x5c.Values - .Select(x => new X509Certificate2(x.GetByteString())) - .ToArray(); - } - // If ecdaaKeyId is present, then the attestation type is ECDAA - else if (null != ecdaaKeyId) - { - // Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash - // using ECDAA-Verify with ECDAA-Issuer public key identified by ecdaaKeyId - // https://www.w3.org/TR/webauthn/#biblio-fidoecdaaalgorithm - - throw new Fido2VerificationException("ECDAA is not yet implemented"); - // If successful, return attestation type ECDAA and attestation trust path ecdaaKeyId. - //attnType = AttestationType.ECDAA; - //trustPath = ecdaaKeyId; - } - // If neither x5c nor ecdaaKeyId is present, self attestation is in use - else - { - // Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData - if (alg.AsInt32() != coseAlg) throw new Fido2VerificationException("Algorithm mismatch between credential public key and authenticator data in self attestation statement"); - // Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash using the credential public key with alg - if (true != AuthDataHelper.VerifySigWithCoseKey(data, credentialPublicKey, sig.GetByteString())) throw new Fido2VerificationException("Failed to validate signature"); - - // If successful, return attestation type Self and empty attestation trust path. - attnType = AttestationType.Self; - trustPath = null; - } - } + verifier = new Packed(AttestationObject.AttStmt, AttestationObject.AuthData, clientDataHash); break; default: throw new Fido2VerificationException("Missing or unknown attestation type"); } + attFmtVerificationResult = verifier.Verify(); /* * 15 - * If validation is successful, obtain a list of acceptable trust anchors (attestation root certificates or ECDAA-Issuer public keys) for that attestation type and attestation statement format fmt, from a trusted source or from policy. For example, the FIDO Metadata Service [FIDOMetadataService] provides one way to obtain such information, using the aaguid in the attestedCredentialData in authData. + * If validation is successful, obtain a list of acceptable trust anchors (attestation root certificates or ECDAA-Issuer public keys) + * for that attestation type and attestation statement format fmt, from a trusted source or from policy. + * For example, the FIDO Metadata Service [FIDOMetadataService] provides one way to obtain such information, + * using the aaguid in the attestedCredentialData in authData. * */ // MetadataService @@ -540,10 +180,10 @@ public async Task VerifyAsync(CredentialCreateOp // todo: implement (this is not for attfmt none) // use aaguid (authData.AttData.Aaguid) to find root certs in metadata // use root plus trustPath to build trust chain - + if (null != metadataService) { - MetadataTOCPayloadEntry entry = metadataService.GetEntry(authData.AttData.GuidAaguid); + var entry = metadataService.GetEntry(authData.AttData.GuidAaguid); if (null != entry && null != entry.MetadataStatement) { @@ -551,23 +191,23 @@ public async Task VerifyAsync(CredentialCreateOp var hasBasicFull = entry.MetadataStatement.AttestationTypes.Contains((ushort)MetadataAttestationType.ATTESTATION_BASIC_FULL); if (false == hasBasicFull && - null != trustPath && - trustPath.FirstOrDefault().Subject != trustPath.FirstOrDefault().Issuer) throw new Fido2VerificationException("Attestation with full attestation from authentictor that does not support full attestation"); - if (true == hasBasicFull && null != trustPath && trustPath.FirstOrDefault().Subject != trustPath.FirstOrDefault().Issuer) + null != attFmtVerificationResult.trustPath && + attFmtVerificationResult.trustPath.FirstOrDefault().Subject != attFmtVerificationResult.trustPath.FirstOrDefault().Issuer) throw new Fido2VerificationException("Attestation with full attestation from authentictor that does not support full attestation"); + if (true == hasBasicFull && null != attFmtVerificationResult.trustPath && attFmtVerificationResult.trustPath.FirstOrDefault().Subject != attFmtVerificationResult.trustPath.FirstOrDefault().Issuer) { var root = new X509Certificate2(Convert.FromBase64String(entry.MetadataStatement.AttestationRootCertificates.FirstOrDefault())); var chain = new X509Chain(); chain.ChainPolicy.ExtraStore.Add(root); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; - if (trustPath.Length > 1) + if (attFmtVerificationResult.trustPath.Length > 1) { - foreach (var cert in trustPath.Skip(1).Reverse()) + foreach (var cert in attFmtVerificationResult.trustPath.Skip(1).Reverse()) { chain.ChainPolicy.ExtraStore.Add(cert); } } - var valid = chain.Build(trustPath[0]); + var valid = chain.Build(attFmtVerificationResult.trustPath[0]); if (false == valid) { @@ -576,7 +216,8 @@ public async Task VerifyAsync(CredentialCreateOp foreach (var report in entry.StatusReports) { - if (true == Enum.IsDefined(typeof(UndesiredAuthenticatorStatus), (UndesiredAuthenticatorStatus) report.Status)) throw new Fido2VerificationException("Authenticator found with undesirable status"); + if (true == Enum.IsDefined(typeof(UndesiredAuthenticatorStatus), (UndesiredAuthenticatorStatus) report.Status)) + throw new Fido2VerificationException("Authenticator found with undesirable status"); } } } @@ -586,7 +227,7 @@ public async Task VerifyAsync(CredentialCreateOp * Check that the credentialId is not yet registered to any other user. * If registration is requested for a credential that is already registered to a different user, the Relying Party SHOULD fail this registration ceremony, or it MAY decide to accept the registration, e.g. while deleting the older registration. * */ - if (false == await isCredentialIdUniqueToUser(new IsCredentialIdUniqueToUserParams(credentialId, originalOptions.User))) + if (false == await isCredentialIdUniqueToUser(new IsCredentialIdUniqueToUserParams(authData.AttData.CredentialID, originalOptions.User))) { throw new Fido2VerificationException("CredentialId is not unique to this user"); } @@ -607,10 +248,10 @@ public async Task VerifyAsync(CredentialCreateOp var result = new AttestationVerificationSuccess() { - CredentialId = credentialId, - PublicKey = credentialPublicKeyBytes, + CredentialId = authData.AttData.CredentialID, + PublicKey = authData.AttData.CredentialPublicKey, User = originalOptions.User, - Counter = BitConverter.ToUInt32(authData.SignCount.ToArray().Reverse().ToArray(), 0) + Counter = BitConverter.ToUInt32(authData.SignCount.Reverse().ToArray(), 0) }; return result; diff --git a/fido2-net-lib/CryptoUtils.cs b/fido2-net-lib/CryptoUtils.cs new file mode 100644 index 00000000..981eabda --- /dev/null +++ b/fido2-net-lib/CryptoUtils.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using PeterO.Cbor; + +namespace Fido2NetLib +{ + public static class CryptoUtils + { + public static HashAlgorithm GetHasher(HashAlgorithmName hashName) + { + switch (hashName.Name) + { + case "SHA1": + return SHA1.Create(); + case "SHA256": + case "HS256": + case "RS256": + case "ES256": + case "PS256": + return SHA256.Create(); + case "SHA384": + case "HS384": + case "RS384": + case "ES384": + case "PS384": + return SHA384.Create(); + case "SHA512": + case "HS512": + case "RS512": + case "ES512": + case "PS512": + return SHA512.Create(); + default: + throw new ArgumentOutOfRangeException("hashName"); + } + } + public static readonly Dictionary algMap = new Dictionary + { + {-65535, HashAlgorithmName.SHA1 }, + {-7, HashAlgorithmName.SHA256}, + {-35, HashAlgorithmName.SHA384 }, + {-36, HashAlgorithmName.SHA512 }, + {-37, HashAlgorithmName.SHA256 }, + {-38, HashAlgorithmName.SHA384 }, + {-39, HashAlgorithmName.SHA512 }, + {-257, HashAlgorithmName.SHA256 }, + {-258, HashAlgorithmName.SHA384 }, + {-259, HashAlgorithmName.SHA512 }, + {4, HashAlgorithmName.SHA1 }, + {11, HashAlgorithmName.SHA256 }, + {12, HashAlgorithmName.SHA384 }, + {13, HashAlgorithmName.SHA512 } + }; + public static readonly Dictionary CoseKeyTypeFromOid = new Dictionary + { + { "1.2.840.10045.2.1", 2 }, // ECC + { "1.2.840.113549.1.1.1", 3} // RSA + }; + public static readonly Dictionary CoseCurveToTpm = new Dictionary + { + { 1, TpmEccCurve.TPM_ECC_NIST_P256}, + { 2, TpmEccCurve.TPM_ECC_NIST_P384}, + { 3, TpmEccCurve.TPM_ECC_NIST_P521} + }; + public static CBORObject CoseKeyFromCertAndAlg(X509Certificate2 cert, int alg) + { + var coseKey = CBORObject.NewMap(); + var kty = CoseKeyTypeFromOid[cert.GetKeyAlgorithm()]; + coseKey.Add(1, kty); + coseKey.Add(3, alg); + if (3 == kty) + { + var keyParams = cert.GetRSAPublicKey().ExportParameters(false); + coseKey.Add(-1, keyParams.Modulus); + coseKey.Add(-2, keyParams.Exponent); + } + if (2 == kty) + { + var ecDsaPubKey = (ECDsaCng)cert.GetECDsaPublicKey(); + var keyParams = ecDsaPubKey.ExportParameters(false); + if (keyParams.Curve.Oid.FriendlyName.Equals(ECCurve.NamedCurves.nistP256.Oid.FriendlyName)) coseKey.Add(-1, 1); + if (keyParams.Curve.Oid.FriendlyName.Equals(ECCurve.NamedCurves.nistP384.Oid.FriendlyName)) coseKey.Add(-1, 2); + if (keyParams.Curve.Oid.FriendlyName.Equals(ECCurve.NamedCurves.nistP521.Oid.FriendlyName)) coseKey.Add(-1, 3); + coseKey.Add(-2, keyParams.Q.X); + coseKey.Add(-3, keyParams.Q.Y); + } + return coseKey; + } + public static bool VerifySigWithCoseKey(byte[] data, CBORObject coseKey, byte[] sig) + { + // Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData + var kty = coseKey[CBORObject.FromObject(1)].AsInt32(); + var alg = coseKey[CBORObject.FromObject(3)].AsInt32(); + var crv = 0; + if (1 == kty || 2 == kty) crv = coseKey[CBORObject.FromObject(-1)].AsInt32(); + switch (kty) // https://www.iana.org/assignments/cose/cose.xhtml#key-type + { + case 1: // OKP + { + switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms + { + case -8: + switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves + { + case 6: + return Chaos.NaCl.Ed25519.Verify(sig, GetHasher(HashAlgorithmName.SHA512).ComputeHash(data), coseKey[CBORObject.FromObject(-2)].GetByteString()); + default: + throw new ArgumentOutOfRangeException("crv"); + } + default: + throw new ArgumentOutOfRangeException("alg"); + } + } + case 2: // EC2 + { + var point = new ECPoint + { + X = coseKey[CBORObject.FromObject(-2)].GetByteString(), + Y = coseKey[CBORObject.FromObject(-3)].GetByteString() + }; + ECCurve curve; + switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms + { + case -7: + switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves + { + case 1: + case 8: + curve = ECCurve.NamedCurves.nistP256; + break; + default: + throw new ArgumentOutOfRangeException("crv"); + } + break; + case -35: + switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves + { + case 2: + curve = ECCurve.NamedCurves.nistP384; + break; + default: + throw new ArgumentOutOfRangeException("crv"); + } + break; + case -36: + switch (crv) // https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves + { + case 3: + curve = ECCurve.NamedCurves.nistP521; + System.Diagnostics.Debug.WriteLine(BitConverter.ToString(sig).Replace("-", "")); + break; + default: + throw new ArgumentOutOfRangeException("crv"); + } + break; + default: + throw new ArgumentOutOfRangeException("alg"); + } + var cng = ECDsa.Create(new ECParameters + { + Q = point, + Curve = curve + }); + var ecsig = SigFromEcDsaSig(sig); + System.Diagnostics.Debug.WriteLine(BitConverter.ToString(ecsig).Replace("-", "")); + return cng.VerifyData(data, ecsig, algMap[alg]); + } + case 3: // RSA + { + RSACng rsa = new RSACng(); + rsa.ImportParameters( + new RSAParameters() + { + Modulus = coseKey[CBORObject.FromObject(-1)].GetByteString(), + Exponent = coseKey[CBORObject.FromObject(-2)].GetByteString() + } + ); + RSASignaturePadding padding; + switch (alg) // https://www.iana.org/assignments/cose/cose.xhtml#algorithms + { + + case -37: + case -38: + case -39: + padding = RSASignaturePadding.Pss; + break; + + case -65535: + case -257: + case -258: + case -259: + padding = RSASignaturePadding.Pkcs1; + break; + default: + throw new ArgumentOutOfRangeException("alg"); + } + return rsa.VerifyData(data, sig, algMap[alg], padding); + } + } + throw new Fido2VerificationException("Missing or unknown keytype"); + } + public static Memory U2FKeyFromCOSEKey(CBORObject COSEKey) + { + var x = COSEKey[CBORObject.FromObject(-2)].GetByteString(); + var y = COSEKey[CBORObject.FromObject(-3)].GetByteString(); + var publicKeyU2F = new byte[1] { 0x4 }; // uncompressed + return publicKeyU2F.Concat(x).Concat(y).ToArray(); + } + + public static byte[] SigFromEcDsaSig(byte[] ecDsaSig) + { + // sanity check of input data + if (null == ecDsaSig || 0 == ecDsaSig.Length || ecDsaSig.Length > ushort.MaxValue) throw new Fido2VerificationException("Invalid ECDsa signature value"); + // first byte should be DER sequence marker + var offset = 0; + var derSequence = AuthDataHelper.GetSizedByteArray(ecDsaSig, ref offset, 1); + if (null == derSequence || 0x30 != derSequence[0]) throw new Fido2VerificationException("ECDsa signature not a valid DER sequence"); + // two forms of length, short form and long form + // short form, one byte, bit 8 not set, rest of the bits indicate data length + var dataLen = AuthDataHelper.GetSizedByteArray(ecDsaSig, ref offset, 1); + if (null == dataLen) throw new Fido2VerificationException("ECDsa signature has invalid length"); + // long form, first byte, bit 8 is set, rest of bits indicate the length of the data length + // so if bit 8 is on... + var longForm = (0 != (dataLen[0] & (1 << 7))); + if (true == longForm) + { + // rest of bits indicate the length of the data length + var longLen = (dataLen[0] & ~(1 << 7)); + // sanity check of input data + if (ushort.MinValue > longLen || ushort.MaxValue < longLen) throw new Fido2VerificationException("ECDsa signature has invalid long form length"); + // total length of remaining data + var longLenByte = AuthDataHelper.GetSizedByteArray(ecDsaSig, ref offset, (ushort)longLen); + if (null == longLenByte) throw new Fido2VerificationException("ECDsa signature has invalid long form length"); + longLen = longLenByte[0]; + // sanity check the length + if (ecDsaSig.Length != (offset + longLen)) throw new Fido2VerificationException("ECDsa signature has invalid long form length"); + } + + // Get R value + var r = GetEcDsaSigValue(ecDsaSig, ref offset, longForm); + if (null == r) throw new Fido2VerificationException("ECDsa signature R integer value invalid"); + + // Get S value + var s = GetEcDsaSigValue(ecDsaSig, ref offset, longForm); + if (null == s) throw new Fido2VerificationException("ECDsa signature S integer value invalid"); + + // make sure we are at the end + if (ecDsaSig.Length != offset) throw new Fido2VerificationException("ECDsa signature has bytes leftover after parsing R and S values"); + + // combine the coordinates and return the raw sign + var sig = new byte[s.Length + r.Length]; + Buffer.BlockCopy(r, 0, sig, 0, r.Length); + Buffer.BlockCopy(s, 0, sig, r.Length, s.Length); + return sig; + } + public static byte[] GetEcDsaSigValue(byte[] ecDsaSig, ref int offset, bool longForm) + { + var derInt = AuthDataHelper.GetSizedByteArray(ecDsaSig, ref offset, 1); + if (null == derInt || 0x02 != derInt[0]) throw new Fido2VerificationException("ECDsa signature coordinate sequence does not contain DER integer value"); // DER INTEGER + var lenByte = AuthDataHelper.GetSizedByteArray(ecDsaSig, ref offset, 1); + if (null == lenByte) throw new Fido2VerificationException("ECDsa signature coordinate integer size invalid"); + var len = (ushort)lenByte[0]; + if (false == longForm) + { + /* + * Ecdsa-Sig-Value ::= SEQUENCE { + * r INTEGER, + * s INTEGER } + * + * From: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer + * + * "Integer values are encoded into a TLV triplet that begins with a Tag value of 0x02. + * The Value field of the TLV triplet contains the encoded integer if it is positive, + * or its two's complement if it is negative. If the integer is positive but the high + * order bit is set to 1, a leading 0x00 is added to the content to indicate that the + * number is not negative." + * + */ + if (0x00 == ecDsaSig[offset] && ((ecDsaSig[offset + 1] & (1 << 7)) != 0)) + { + offset++; + len--; + } + } + return AuthDataHelper.GetSizedByteArray(ecDsaSig, ref offset, len); + } + } +} diff --git a/fido2-net-lib/MetadataService.cs b/fido2-net-lib/MetadataService.cs index a23720c4..2b280f23 100644 --- a/fido2-net-lib/MetadataService.cs +++ b/fido2-net-lib/MetadataService.cs @@ -313,8 +313,7 @@ public interface IMetadataService public sealed class MDSMetadata : IMetadataService { private static volatile MDSMetadata mDSMetadata; - private static object syncRoot = new System.Object(); - public static readonly string mds1url = "https://mds.fidoalliance.org"; + private static object syncRoot = new object(); public static readonly string mds2url = "https://mds2.fidoalliance.org"; public static readonly string tokenParamName = "/?token="; private static string _accessToken; @@ -341,7 +340,7 @@ private MDSMetadata(string accessToken, string cachedirPath) { GetTOCPayload(true); } - catch (System.Exception ex) + catch (Exception ex) { if (ex is Fido2VerificationException || ex is System.IO.FileNotFoundException) { } else throw; @@ -483,7 +482,7 @@ private MetadataStatement GetMetadataStatement(MetadataTOCPayloadEntry entry, bo var statementBytes = Base64Url.Decode(rawStatement); var statement = System.Text.Encoding.UTF8.GetString(statementBytes, 0, statementBytes.Length); var ret = JsonConvert.DeserializeObject(statement); - ret.Hash = Base64Url.Encode(AuthDataHelper.GetHasher(new HashAlgorithmName(tocAlg)).ComputeHash(System.Text.Encoding.UTF8.GetBytes(rawStatement))); + ret.Hash = Base64Url.Encode(CryptoUtils.GetHasher(new HashAlgorithmName(tocAlg)).ComputeHash(System.Text.Encoding.UTF8.GetBytes(rawStatement))); return ret; } public void GetTOCPayload(bool fromCache) diff --git a/fido2-net-lib/Objects/AttestationFormatVerificationResult.cs b/fido2-net-lib/Objects/AttestationFormatVerificationResult.cs new file mode 100644 index 00000000..6e7fda26 --- /dev/null +++ b/fido2-net-lib/Objects/AttestationFormatVerificationResult.cs @@ -0,0 +1,10 @@ +using System.Security.Cryptography.X509Certificates; + +namespace Fido2NetLib.Objects +{ + public class AttestationFormatVerificationResult + { + public AttestationType attnType; + public X509Certificate2[] trustPath; + } +} From c3313a152a40bb78e915c4cb772cc9ba003438ea Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Wed, 24 Oct 2018 20:15:01 -0400 Subject: [PATCH 09/10] Additional cleanup and reorganization. Adding a failing test captured from conformance tool ALG_SIGN_SECP521R1_ECDSA_SHA512_RAW. --- fido2-net-lib.Test/UnitTest1.cs | 9 + .../attestationOptionsPacked512.json | 1 + .../attestationResultsPacked512.json | 10 + fido2-net-lib.Test/fido2-net-lib.Test.csproj | 8 + fido2-net-lib/AttestationFormat/AndroidKey.cs | 13 +- .../AttestationFormat/AttestationFormat.cs | 35 ++- fido2-net-lib/AttestationFormat/Packed.cs | 35 ++- fido2-net-lib/AttestationFormat/Tpm.cs | 279 +++++++++++++++++- fido2-net-lib/AuthDataHelper.cs | 112 +------ fido2-net-lib/CryptoUtils.cs | 13 +- fido2-net-lib/TPM.cs | 241 --------------- 11 files changed, 391 insertions(+), 365 deletions(-) create mode 100644 fido2-net-lib.Test/attestationOptionsPacked512.json create mode 100644 fido2-net-lib.Test/attestationResultsPacked512.json delete mode 100644 fido2-net-lib/TPM.cs diff --git a/fido2-net-lib.Test/UnitTest1.cs b/fido2-net-lib.Test/UnitTest1.cs index 807c8f25..1fcc4c63 100644 --- a/fido2-net-lib.Test/UnitTest1.cs +++ b/fido2-net-lib.Test/UnitTest1.cs @@ -203,6 +203,15 @@ public async Task TestAndroidKeyAttestationAsync() await o.VerifyAsync(options, "https://localhost:44329", (x) => Task.FromResult(true), null, null); byte[] ad = o.AttestationObject.AuthData; } + [Fact] + public async Task TaskPackedAttestation512() + { + var jsonPost = JsonConvert.DeserializeObject(File.ReadAllText("./attestationResultsPacked512.json")); + var options = JsonConvert.DeserializeObject(File.ReadAllText("./attestationOptionsPacked512.json")); + var o = AuthenticatorAttestationResponse.Parse(jsonPost); + await o.VerifyAsync(options, "https://localhost:44329", (x) => Task.FromResult(true), null, null); + byte[] ad = o.AttestationObject.AuthData; + } //public void TestHasCorrentAAguid() //{ // var expectedAaguid = new Uint8Array([ diff --git a/fido2-net-lib.Test/attestationOptionsPacked512.json b/fido2-net-lib.Test/attestationOptionsPacked512.json new file mode 100644 index 00000000..823821c6 --- /dev/null +++ b/fido2-net-lib.Test/attestationOptionsPacked512.json @@ -0,0 +1 @@ +{"rp":{"id":"localhost","name":"Fido2 test"},"user":{"name":"qhpF3If1h4ANVCNzl08m","id":"qhpF3If1h4ANVCNzl08m","displayName":"Shala Dull"},"challenge":"QPA-FrCSwg-qHhzYvRIdnA","pubKeyCredParams":[{"type":"public-key","alg":-7},{"type":"public-key","alg":-257},{"type":"public-key","alg":-37},{"type":"public-key","alg":-35},{"type":"public-key","alg":-258},{"type":"public-key","alg":-38},{"type":"public-key","alg":-36},{"type":"public-key","alg":-259},{"type":"public-key","alg":-39},{"type":"public-key","alg":-65535}],"timeout":60000,"attestation":"direct","authenticatorSelection":null,"excludeCredentials":[],"status":"ok","errorMessage":""} \ No newline at end of file diff --git a/fido2-net-lib.Test/attestationResultsPacked512.json b/fido2-net-lib.Test/attestationResultsPacked512.json new file mode 100644 index 00000000..d1084098 --- /dev/null +++ b/fido2-net-lib.Test/attestationResultsPacked512.json @@ -0,0 +1,10 @@ +{ + "rawId": "6YIJExgLDzTvfys9WgQlIGTL1L9Ys9bhaaA1Pr-OAPc", + "id": "6YIJExgLDzTvfys9WgQlIGTL1L9Ys9bhaaA1Pr-OAPc", + "response": { + "clientDataJSON": "eyJvcmlnaW4iOiJodHRwczovL2xvY2FsaG9zdDo0NDMyOSIsImNoYWxsZW5nZSI6IlFQQS1GckNTd2ctcUhoell2UklkbkEiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0", + "attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzgjY3NpZ1iKMIGHAkE9Vr0j3zGzH6_YASuNse-D4bIDPU4ralNkJqgbCyv_tPNdt27VKaPDnK3WKWgv1qna04qMA7yukZeOPods8arRVQJCAZibACvAfmwBNT4cvR32MNvgGienLXmi2q8MwytcGrtOMnyhnxgco0pOFH7eWHXzn64mVqdSD-wPRTIfJ3McBxW0aGF1dGhEYXRhWOlJlg3liA6MaHQ0Fw9kdmBbj-SuuaKGMseZXPO6gx2XY0EAAABmI4irjYkVQUaTutQ-Zx0lOAAg6YIJExgLDzTvfys9WgQlIGTL1L9Ys9bhaaA1Pr-OAPelAQIDOCMgAyFYQgGzEwyupDz8u1IHtClxewg8CYWBRqD6_SufCj6-LevV57awHyeFGbyfS78ZB4e_I7RmndDI-jO24T3WZ1JMoE1mMCJYQgCpx32yAvYCfKWILgd5aLYuE5L8lEWuN5lhzGwNXoi6pj0JcQR60yCzI8HPlESzEvpqtCNBqF99eD2JETVIqkiwvQ" + }, + "type": "public-key" +} + diff --git a/fido2-net-lib.Test/fido2-net-lib.Test.csproj b/fido2-net-lib.Test/fido2-net-lib.Test.csproj index 2a1820bb..003431c3 100644 --- a/fido2-net-lib.Test/fido2-net-lib.Test.csproj +++ b/fido2-net-lib.Test/fido2-net-lib.Test.csproj @@ -15,9 +15,11 @@ + + @@ -59,6 +61,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -68,6 +73,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/fido2-net-lib/AttestationFormat/AndroidKey.cs b/fido2-net-lib/AttestationFormat/AndroidKey.cs index 07e96845..ae572a05 100644 --- a/fido2-net-lib/AttestationFormat/AndroidKey.cs +++ b/fido2-net-lib/AttestationFormat/AndroidKey.cs @@ -10,6 +10,17 @@ namespace Fido2NetLib.AttestationFormat { class AndroidKey : AttestationFormat { + public static byte[] AttestationExtensionBytes(X509ExtensionCollection exts) + { + foreach (var ext in exts) + { + if (ext.Oid.Value.Equals("1.3.6.1.4.1.11129.2.1.17")) // AttestationRecordOid + { + return ext.RawData; + } + } + return null; + } public static byte[] GetASN1ObjectAtIndex(byte[] attExtBytes, int index) { // https://developer.android.com/training/articles/security-key-attestation#certificate_schema @@ -209,7 +220,7 @@ public override AttestationFormatVerificationResult Verify() throw new Fido2VerificationException("Invalid android key signature"); // Verify that in the attestation certificate extension data: - var attExtBytes = AuthDataHelper.AttestationExtensionBytes(androidKeyCert.Extensions); + var attExtBytes = AttestationExtensionBytes(androidKeyCert.Extensions); // 1. The value of the attestationChallenge field is identical to clientDataHash. var attestationChallenge = GetAttestationChallenge(attExtBytes); diff --git a/fido2-net-lib/AttestationFormat/AttestationFormat.cs b/fido2-net-lib/AttestationFormat/AttestationFormat.cs index cb0efa91..cf3a952f 100644 --- a/fido2-net-lib/AttestationFormat/AttestationFormat.cs +++ b/fido2-net-lib/AttestationFormat/AttestationFormat.cs @@ -1,6 +1,8 @@ using PeterO.Cbor; using Fido2NetLib.Objects; using System; +using System.Security.Cryptography.X509Certificates; +using System.Linq; namespace Fido2NetLib.AttestationFormat { @@ -31,7 +33,38 @@ internal byte[] Data return data; } } - + internal static byte[] AaguidFromAttnCertExts(X509ExtensionCollection exts) + { + byte[] aaguid = null; + foreach (var ext in exts) + { + if (ext.Oid.Value.Equals("1.3.6.1.4.1.45724.1.1.4")) // id-fido-gen-ce-aaguid + { + aaguid = new byte[16]; + var ms = new System.IO.MemoryStream(ext.RawData.ToArray()); + // OCTET STRING + if (0x4 != ms.ReadByte()) throw new Fido2VerificationException("Expected octet string value"); + // AAGUID + if (0x10 != ms.ReadByte()) throw new Fido2VerificationException("Unexpected length for aaguid"); + ms.Read(aaguid, 0, 0x10); + //The extension MUST NOT be marked as critical + if (true == ext.Critical) throw new Fido2VerificationException("extension MUST NOT be marked as critical"); + } + } + return aaguid; + } + internal static bool IsAttnCertCACert(X509ExtensionCollection exts) + { + foreach (var ext in exts) + { + if (ext.Oid.FriendlyName == "Basic Constraints") + { + var baseExt = (X509BasicConstraintsExtension)ext; + return baseExt.CertificateAuthority; + } + } + return true; + } public abstract AttestationFormatVerificationResult Verify(); } } diff --git a/fido2-net-lib/AttestationFormat/Packed.cs b/fido2-net-lib/AttestationFormat/Packed.cs index 395aa58c..e6730b87 100644 --- a/fido2-net-lib/AttestationFormat/Packed.cs +++ b/fido2-net-lib/AttestationFormat/Packed.cs @@ -12,6 +12,33 @@ class Packed : AttestationFormat public Packed(CBORObject attStmt, byte[] authenticatorData, byte[] clientDataHash) : base(attStmt, authenticatorData, clientDataHash) { } + public static int U2FTransportsFromAttnCert(X509ExtensionCollection exts) + { + var u2ftransports = 0; + foreach (var ext in exts) + { + if (ext.Oid.Value.Equals("1.3.6.1.4.1.45724.2.1.1")) + { + var ms = new System.IO.MemoryStream(ext.RawData.ToArray()); + // BIT STRING + if (0x3 != ms.ReadByte()) throw new Fido2VerificationException("Expected bit string"); + if (0x2 != ms.ReadByte()) throw new Fido2VerificationException("Expected integer value"); + var unused = ms.ReadByte(); // unused byte + // https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-authenticator-transports-extension-v1.1-id-20160915.html#fido-u2f-certificate-transports-extension + u2ftransports = ms.ReadByte(); // do something with this? + } + } + return u2ftransports; + } + public static bool IsValidPackedAttnCertSubject(string attnCertSubj) + { + var dictSubject = attnCertSubj.Split(new string[] { ", " }, StringSplitOptions.None).Select(part => part.Split('=')).ToDictionary(split => split[0], split => split[1]); + return (0 != dictSubject["C"].Length || + 0 != dictSubject["O"].Length || + 0 != dictSubject["OU"].Length || + 0 != dictSubject["CN"].Length || + "Authenticator Attestation" == dictSubject["OU"].ToString()); + } public override AttestationFormatVerificationResult Verify() { // Verify that attStmt is valid CBOR conforming to the syntax defined above and @@ -65,22 +92,22 @@ public override AttestationFormatVerificationResult Verify() // Subject field MUST contain C, O, OU, CN // OU must match "Authenticator Attestation" - if (true != AuthDataHelper.IsValidPackedAttnCertSubject(attestnCert.Subject)) + if (true != IsValidPackedAttnCertSubject(attestnCert.Subject)) throw new Fido2VerificationException("Invalid attestation cert subject"); // 2c. If the related attestation root certificate is used for multiple authenticator models, // the Extension OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) MUST be present, containing the AAGUID as a 16-byte OCTET STRING // verify that the value of this extension matches the aaguid in authenticatorData - var aaguid = AuthDataHelper.AaguidFromAttnCertExts(attestnCert.Extensions); + var aaguid = AaguidFromAttnCertExts(attestnCert.Extensions); if (aaguid != null && !aaguid.SequenceEqual(AuthData.AttData.Aaguid.ToArray())) throw new Fido2VerificationException("aaguid present in packed attestation but does not match aaguid from authData"); // 2d. The Basic Constraints extension MUST have the CA component set to false - if (AuthDataHelper.IsAttnCertCACert(attestnCert.Extensions)) + if (IsAttnCertCACert(attestnCert.Extensions)) throw new Fido2VerificationException("Attestion certificate has CA cert flag present"); // id-fido-u2f-ce-transports - var u2ftransports = AuthDataHelper.U2FTransportsFromAttnCert(attestnCert.Extensions); + var u2ftransports = U2FTransportsFromAttnCert(attestnCert.Extensions); return new AttestationFormatVerificationResult() { diff --git a/fido2-net-lib/AttestationFormat/Tpm.cs b/fido2-net-lib/AttestationFormat/Tpm.cs index 2d966cf9..583dcc5a 100644 --- a/fido2-net-lib/AttestationFormat/Tpm.cs +++ b/fido2-net-lib/AttestationFormat/Tpm.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Fido2NetLib.Objects; using PeterO.Cbor; @@ -45,7 +47,7 @@ public override AttestationFormatVerificationResult Verify() var X = CredentialPublicKey[CBORObject.FromObject(-2)].GetByteString(); var Y = CredentialPublicKey[CBORObject.FromObject(-3)].GetByteString(); - if (pubArea.EccCurve != CryptoUtils.CoseCurveToTpm[curve]) throw new Fido2VerificationException("Curve mismatch between pubArea and credentialPublicKey"); + if (pubArea.EccCurve != CoseCurveToTpm[curve]) throw new Fido2VerificationException("Curve mismatch between pubArea and credentialPublicKey"); if (!pubArea.ECPoint.X.SequenceEqual(X)) throw new Fido2VerificationException("X-coordinate mismatch between pubArea and credentialPublicKey"); if (!pubArea.ECPoint.Y.SequenceEqual(Y)) throw new Fido2VerificationException("Y-coordinate mismatch between pubArea and credentialPublicKey"); } @@ -100,7 +102,7 @@ public override AttestationFormatVerificationResult Verify() // The Subject Alternative Name extension MUST be set as defined in [TPMv2-EK-Profile] section 3.2.9. // https://www.w3.org/TR/webauthn/#tpm-cert-requirements - var SAN = AuthDataHelper.SANFromAttnCertExts(aikCert.Extensions); + var SAN = SANFromAttnCertExts(aikCert.Extensions); if (null == SAN || 0 == SAN.Length) throw new Fido2VerificationException("SAN missing from TPM attestation certificate"); @@ -117,16 +119,16 @@ public override AttestationFormatVerificationResult Verify() // The Extended Key Usage extension MUST contain the "joint-iso-itu-t(2) internationalorganizations(23) 133 tcg-kp(8) tcg-kp-AIKCertificate(3)" OID. // OID is 2.23.133.8.3 - var EKU = AuthDataHelper.EKUFromAttnCertExts(aikCert.Extensions); + var EKU = EKUFromAttnCertExts(aikCert.Extensions); if (null == EKU || 0 != EKU.CompareTo("Attestation Identity Key Certificate (2.23.133.8.3)")) throw new Fido2VerificationException("Invalid EKU on AIK certificate"); // The Basic Constraints extension MUST have the CA component set to false. - if (AuthDataHelper.IsAttnCertCACert(aikCert.Extensions)) + if (IsAttnCertCACert(aikCert.Extensions)) throw new Fido2VerificationException("aikCert Basic Constraints extension CA component must be false"); // If aikCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) verify that the value of this extension matches the aaguid in authenticatorData - var aaguid = AuthDataHelper.AaguidFromAttnCertExts(aikCert.Extensions); + var aaguid = AaguidFromAttnCertExts(aikCert.Extensions); if ((null != aaguid) && (!aaguid.SequenceEqual(Guid.Empty.ToByteArray())) && (!aaguid.SequenceEqual(AuthData.AttData.Aaguid.ToArray()))) throw new Fido2VerificationException("aaguid malformed"); // If successful, return attestation type AttCA and attestation trust path x5c. @@ -151,5 +153,272 @@ public override AttestationFormatVerificationResult Verify() else throw new Fido2VerificationException("Neither x5c nor ECDAA were found in the TPM attestation statement"); } + private static readonly Dictionary CoseCurveToTpm = new Dictionary + { + { 1, TpmEccCurve.TPM_ECC_NIST_P256}, + { 2, TpmEccCurve.TPM_ECC_NIST_P384}, + { 3, TpmEccCurve.TPM_ECC_NIST_P521} + }; + private static string SANFromAttnCertExts(X509ExtensionCollection exts) + { + foreach (var ext in exts) + { + if (ext.Oid.Value.Equals("2.5.29.17")) // subject alternative name + { + var asn = new AsnEncodedData(ext.Oid, ext.RawData); + return asn.Format(true); + } + } + return null; + } + private static string EKUFromAttnCertExts(X509ExtensionCollection exts) + { + foreach (var ext in exts) + { + if (ext.Oid.Value.Equals("2.5.29.37")) // EKU + { + var asn = new AsnEncodedData(ext.Oid, ext.RawData); + return asn.Format(false); + } + } + return null; + } + } + + public enum TpmEccCurve : ushort + { + // TCG TPM Rev 2.0, part 2, structures, section 6.4, TPM_ECC_CURVE + TPM_ECC_NONE, // 0x0000 + TPM_ECC_NIST_P192, // 0x0001 + TPM_ECC_NIST_P224, // 0x0002 + TPM_ECC_NIST_P256, // 0x0003 + TPM_ECC_NIST_P384, // 0x0004 + TPM_ECC_NIST_P521, // 0x0005 + TPM_ECC_BN_P256, // 0x0010 curve to support ECDAA + TPM_ECC_BN_P638, // 0x0011 curve to support ECDAA + TPM_ECC_SM2_P256 // 0x0020 + } + public enum TpmAlg : ushort + { + // TCG TPM Rev 2.0, part 2, structures, section 6.3, TPM_ALG_ID + TPM_ALG_ERROR, // 0 + TPM_ALG_RSA, // 1 + TPM_ALG_SHA1 = 4, // 4 + TPM_ALG_HMAC, // 5 + TPM_ALG_AES, // 6 + TPM_ALG_MGF1, // 7 + TPM_ALG_KEYEDHASH, // 8 + TPM_ALG_XOR = 0xA, // A + TPM_ALG_SHA256, // B + TPM_ALG_SHA384, // C + TPM_ALG_SHA512, // D + TPM_ALG_NULL = 0x10, // 10 + TPM_ALG_SM3_256 = 0x12, // 12 + TPM_ALG_SM4, // 13 + TPM_ALG_RSASSA, // 14 + TPM_ALG_RSAES, // 15 + TPM_ALG_RSAPSS, // 16 + TPM_ALG_OAEP, // 17 + TPM_ALG_ECDSA, // 18 + TPM_ALG_ECDH, // 19 + TPM_ALG_ECDAA, // 1A + TPM_ALG_SM2, // 1B + TPM_ALG_ECSCHNORR, // 1C + TPM_ALG_ECMQV, // 1D + TPM_ALG_KDF1_SP800_56A = 0x20, + TPM_ALG_KDF2, // 21 + TPM_ALG_KDF1_SP800_108, // 22 + TPM_ALG_ECC, // 23 + TPM_ALG_SYMCIPHER = 0x25, + TPM_ALG_CAMELLIA, // 26 + TPM_ALG_CTR = 0x40, + TPM_ALG_OFB, // 41 + TPM_ALG_CBC, // 42 + TPM_ALG_CFB, // 43 + TPM_ALG_ECB // 44 + }; + // TPMS_ATTEST, TPMv2-Part2, section 10.12.8 + public class CertInfo + { + private static readonly Dictionary tpmAlgToDigestSizeMap = new Dictionary + { + {TpmAlg.TPM_ALG_SHA1, (160/8) }, + {TpmAlg.TPM_ALG_SHA256, (256/8) }, + {TpmAlg.TPM_ALG_SHA384, (384/8) }, + {TpmAlg.TPM_ALG_SHA512, (512/8) } + }; + public static (ushort size, byte[] name) NameFromTPM2BName(Memory ab, ref int offset) + { + // TCG TPM Rev 2.0, part 2, structures, section 10.5.3, TPM2B_NAME + // This buffer holds a Name for any entity type. + // The type of Name in the structure is determined by context and the size parameter. + var totalBytes = AuthDataHelper.GetSizedByteArray(ab, ref offset, 2); + ushort totalSize = 0; + if (null != totalBytes) + { + totalSize = BitConverter.ToUInt16(totalBytes.ToArray().Reverse().ToArray(), 0); + } + ushort size = 0; + var bytes = AuthDataHelper.GetSizedByteArray(ab, ref offset, 2); + if (null != bytes) + { + size = BitConverter.ToUInt16(bytes.ToArray().Reverse().ToArray(), 0); + } + // If size is four, then the Name is a handle. + if (4 == size) throw new Fido2VerificationException("Unexpected handle in TPM2B_NAME"); + // If size is zero, then no Name is present. + if (0 == size) throw new Fido2VerificationException("Unexpected no name found in TPM2B_NAME"); + // Otherwise, the size shall be the size of a TPM_ALG_ID plus the size of the digest produced by the indicated hash algorithm. + var tpmalg = TpmAlg.TPM_ALG_ERROR; + byte[] name = null; + if (Enum.IsDefined(typeof(TpmAlg), size)) + { + tpmalg = (TpmAlg)size; + if (tpmAlgToDigestSizeMap.ContainsKey(tpmalg)) + { + name = AuthDataHelper.GetSizedByteArray(ab, ref offset, tpmAlgToDigestSizeMap[tpmalg]); + } + } + if (totalSize != bytes.Length + name.Length) throw new Fido2VerificationException("Unexpected no name found in TPM2B_NAME"); + return (size, name); + } + public CertInfo(byte[] certInfo) + { + if (null == certInfo || 0 == certInfo.Length) throw new Fido2VerificationException("Malformed certInfo bytes"); + Raw = certInfo; + var offset = 0; + Magic = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); + if (0xff544347 != BitConverter.ToUInt32(Magic.ToArray().Reverse().ToArray(), 0)) throw new Fido2VerificationException("Bad magic number " + Magic.ToString()); + Type = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 2); + if (0x8017 != BitConverter.ToUInt16(Type.ToArray().Reverse().ToArray(), 0)) throw new Fido2VerificationException("Bad structure tag " + Type.ToString()); + QualifiedSigner = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); + ExtraData = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); + if (null == ExtraData || 0 == ExtraData.Length) throw new Fido2VerificationException("Bad extraData in certInfo"); + Clock = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 8); + ResetCount = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); + RestartCount = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); + Safe = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 1); + FirmwareVersion = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 8); + var (size, name) = NameFromTPM2BName(certInfo, ref offset); + Alg = size; // TPM_ALG_ID + AttestedName = name; + AttestedQualifiedNameBuffer = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); + if (certInfo.Length != offset) throw new Fido2VerificationException("Leftover bits decoding certInfo"); + } + public byte[] Raw { get; private set; } + public byte[] Magic { get; private set; } + public byte[] Type { get; private set; } + public byte[] QualifiedSigner { get; private set; } + public byte[] ExtraData { get; private set; } + public byte[] Clock { get; private set; } + public byte[] ResetCount { get; private set; } + public byte[] RestartCount { get; private set; } + public byte[] Safe { get; private set; } + public byte[] FirmwareVersion { get; private set; } + public ushort Alg { get; private set; } + public byte[] AttestedName { get; private set; } + public byte[] AttestedQualifiedNameBuffer { get; private set; } + } + // TPMT_PUBLIC, TPMv2-Part2, section 12.2.4 + public class PubArea + { + public PubArea(byte[] pubArea) + { + Raw = pubArea; + var offset = 0; + + // TPMI_ALG_PUBLIC + Type = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + var tpmalg = (TpmAlg)Enum.Parse(typeof(TpmAlg), BitConverter.ToUInt16(Type.Reverse().ToArray(), 0).ToString()); + + // TPMI_ALG_HASH + Alg = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + + // TPMA_OBJECT, attributes that, along with type, determine the manipulations of this object + Attributes = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 4); + + // TPM2B_DIGEST, optional policy for using this key, computed using the alg of the object + Policy = AuthDataHelper.GetSizedByteArray(pubArea, ref offset); + + // TPMU_PUBLIC_PARMS + Symmetric = null; + Scheme = null; + + if (TpmAlg.TPM_ALG_KEYEDHASH == tpmalg) + { + throw new Fido2VerificationException("TPM_ALG_KEYEDHASH not yet supported"); + } + if (TpmAlg.TPM_ALG_SYMCIPHER == tpmalg) + { + throw new Fido2VerificationException("TPM_ALG_SYMCIPHER not yet supported"); + } + + // TPMS_ASYM_PARMS, for TPM_ALG_RSA and TPM_ALG_ECC + if (TpmAlg.TPM_ALG_RSA == tpmalg || TpmAlg.TPM_ALG_ECC == tpmalg) + { + Symmetric = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + Scheme = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + } + + // TPMI_RSA_KEY_BITS, number of bits in the public modulus + KeyBits = null; + // The public exponent, a prime number greater than 2. When zero, indicates that the exponent is the default of 2^16 + 1 + Exponent = 0; + + if (TpmAlg.TPM_ALG_RSA == tpmalg) + { + KeyBits = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + var tmp = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 4); + if (null != tmp) + { + Exponent = BitConverter.ToUInt32(tmp.ToArray(), 0); + if (0 == Exponent) Exponent = Convert.ToUInt32(Math.Pow(2, 16) + 1); + } + } + + // TPMI_ECC_CURVE + CurveID = null; + + // TPMT_KDF_SCHEME, an optional key derivation scheme for generating a symmetric key from a Z value + // If the kdf parameter associated with curveID is not TPM_ALG_NULL then this is required to be NULL. + // NOTE There are currently no commands where this parameter has effect and, in the reference code, this field needs to be set to TPM_ALG_NULL. + KDF = null; + + if (TpmAlg.TPM_ALG_ECC == tpmalg) + { + CurveID = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + KDF = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); + } + + // TPMU_PUBLIC_ID + Unique = AuthDataHelper.GetSizedByteArray(pubArea, ref offset); + if (pubArea.Length != offset) throw new Fido2VerificationException("Leftover bytes decoding pubArea"); + } + public byte[] Raw { get; private set; } + public byte[] Type { get; private set; } + public byte[] Alg { get; private set; } + public byte[] Attributes { get; private set; } + public byte[] Policy { get; private set; } + public byte[] Symmetric { get; private set; } + public byte[] Scheme { get; private set; } + public byte[] KeyBits { get; private set; } + public uint Exponent { get; private set; } + public byte[] CurveID { get; private set; } + public byte[] KDF { get; private set; } + public byte[] Unique { get; private set; } + public TpmEccCurve EccCurve { get { return (TpmEccCurve)Enum.Parse(typeof(TpmEccCurve), BitConverter.ToUInt16(CurveID.Reverse().ToArray(), 0).ToString()); }} + public System.Security.Cryptography.ECPoint ECPoint + { + get + { + var point = new System.Security.Cryptography.ECPoint(); + var uniqueOffset = 0; + var size = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, 2); + point.X = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, BitConverter.ToUInt16(size.Reverse().ToArray(), 0)); + size = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, 2); + point.Y = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, BitConverter.ToUInt16(size.Reverse().ToArray(), 0)); + return point; + } + } } } diff --git a/fido2-net-lib/AuthDataHelper.cs b/fido2-net-lib/AuthDataHelper.cs index a1a9ed25..9b961cdc 100644 --- a/fido2-net-lib/AuthDataHelper.cs +++ b/fido2-net-lib/AuthDataHelper.cs @@ -1,7 +1,5 @@ using System; using System.Linq; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using PeterO.Cbor; namespace Fido2NetLib @@ -11,104 +9,7 @@ namespace Fido2NetLib /// public static class AuthDataHelper { - public static byte[] AaguidFromAttnCertExts(X509ExtensionCollection exts) - { - byte[] aaguid = null; - foreach (var ext in exts) - { - if (ext.Oid.Value.Equals("1.3.6.1.4.1.45724.1.1.4")) // id-fido-gen-ce-aaguid - { - aaguid = new byte[16]; - var ms = new System.IO.MemoryStream(ext.RawData.ToArray()); - // OCTET STRING - if (0x4 != ms.ReadByte()) throw new Fido2VerificationException("Expected octet string value"); - // AAGUID - if (0x10 != ms.ReadByte()) throw new Fido2VerificationException("Unexpected length for aaguid"); - ms.Read(aaguid, 0, 0x10); - //The extension MUST NOT be marked as critical - if (true == ext.Critical) throw new Fido2VerificationException("extension MUST NOT be marked as critical"); - } - } - return aaguid; - } - public static string SANFromAttnCertExts(X509ExtensionCollection exts) - { - foreach (var ext in exts) - { - if (ext.Oid.Value.Equals("2.5.29.17")) // subject alternative name - { - var asn = new AsnEncodedData(ext.Oid, ext.RawData); - return asn.Format(true); - } - } - return null; - } - public static string EKUFromAttnCertExts(X509ExtensionCollection exts) - { - foreach (var ext in exts) - { - if (ext.Oid.Value.Equals("2.5.29.37")) // EKU - { - var asn = new AsnEncodedData(ext.Oid, ext.RawData); - return asn.Format(false); - } - } - return null; - } - public static bool IsAttnCertCACert(X509ExtensionCollection exts) - { - foreach (var ext in exts) - { - if (ext.Oid.FriendlyName == "Basic Constraints") - { - var baseExt = (X509BasicConstraintsExtension)ext; - return baseExt.CertificateAuthority; - } - } - return true; - } - public static byte[] AttestationExtensionBytes(X509ExtensionCollection exts) - { - foreach (var ext in exts) - { - if (ext.Oid.Value.Equals("1.3.6.1.4.1.11129.2.1.17")) // AttestationRecordOid - { - return ext.RawData; - } - } - return null; - } - - public static int U2FTransportsFromAttnCert(X509ExtensionCollection exts) - { - var u2ftransports = 0; - foreach (var ext in exts) - { - if (ext.Oid.Value.Equals("1.3.6.1.4.1.45724.2.1.1")) - { - var ms = new System.IO.MemoryStream(ext.RawData.ToArray()); - // BIT STRING - if (0x3 != ms.ReadByte()) throw new Fido2VerificationException("Expected bit string"); - if (0x2 != ms.ReadByte()) throw new Fido2VerificationException("Expected integer value"); - var unused = ms.ReadByte(); // unused byte - // https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-authenticator-transports-extension-v1.1-id-20160915.html#fido-u2f-certificate-transports-extension - u2ftransports = ms.ReadByte(); // do something with this? - } - } - return u2ftransports; - } - - public static bool IsValidPackedAttnCertSubject(string attnCertSubj) - { - var dictSubject = attnCertSubj.Split(new string[] { ", " }, StringSplitOptions.None).Select(part => part.Split('=')).ToDictionary(split => split[0], split => split[1]); - return (0 != dictSubject["C"].Length || - 0 != dictSubject["O"].Length || - 0 != dictSubject["OU"].Length || - 0 != dictSubject["CN"].Length || - "Authenticator Attestation" == dictSubject["OU"].ToString()); - } - - public static byte[] GetSizedByteArray(Memory ab, ref int offset, UInt16 len = 0) + public static byte[] GetSizedByteArray(Memory ab, ref int offset, ushort len = 0) { if ((0 == len) && ((offset + 2) <= ab.Length)) { @@ -124,7 +25,7 @@ public static byte[] GetSizedByteArray(Memory ab, ref int offset, UInt16 l return result; } } - // https://w3c.github.io/webauthn/#authenticator-data + // https://www.w3.org/TR/webauthn/#sec-authenticator-data public class AuthenticatorData { enum authDataFlags @@ -150,7 +51,7 @@ public AuthenticatorData(byte[] authData) Extensions = null; // Determining attested credential data's length, which is variable, involves determining credentialPublicKey’s beginning location given the preceding credentialId’s length, and then determining the credentialPublicKey’s length if (true == AttestedCredentialDataPresent) AttData = new AttestedCredentialData(authData, ref offset); - if (true == ExtensionsPresent) Extensions = AuthDataHelper.GetSizedByteArray(authData, ref offset, (UInt16) (authData.Length - offset)); + if (true == ExtensionsPresent) Extensions = AuthDataHelper.GetSizedByteArray(authData, ref offset, (ushort) (authData.Length - offset)); if (authData.Length != offset) throw new Fido2VerificationException("Leftover bytes decoding AuthenticatorData"); } public byte[] RpIdHash { get; private set; } @@ -167,7 +68,7 @@ public AuthenticatorData(byte[] authData) public bool AttestedCredentialDataPresent { get { return ((Flags & (1 << (int) authDataFlags.AT)) != 0); } } public bool ExtensionsPresent { get { return ((Flags & (1 << (int) authDataFlags.ED)) != 0); } } } - // https://w3c.github.io/webauthn/#attested-credential-data + // https://www.w3.org/TR/webauthn/#sec-attested-credential-data public class AttestedCredentialData { public static Guid FromBigEndian(byte[] Aaguid) @@ -191,7 +92,6 @@ public AttestedCredentialData(byte[] attData, ref int offset) { Aaguid = AuthDataHelper.GetSizedByteArray(attData, ref offset, 16); if (null == Aaguid) throw new Fido2VerificationException("Attested credential data is invalid"); - GuidAaguid = FromBigEndian(Aaguid); CredentialID = AuthDataHelper.GetSizedByteArray(attData, ref offset); // Determining attested credential data's length, which is variable, involves determining credentialPublicKey’s beginning location given the preceding credentialId’s length, and then determining the credentialPublicKey’s length var ms = new System.IO.MemoryStream(attData, offset, attData.Length - offset); @@ -207,10 +107,10 @@ public AttestedCredentialData(byte[] attData, ref int offset) } var aCDLen = tmp.EncodeToBytes().Length; - CredentialPublicKey = AuthDataHelper.GetSizedByteArray(attData, ref offset, (UInt16)(aCDLen)); + CredentialPublicKey = AuthDataHelper.GetSizedByteArray(attData, ref offset, (ushort)(aCDLen)); if (null == CredentialID || null == CredentialPublicKey) throw new Fido2VerificationException("Attested credential data is invalid"); } - public Guid GuidAaguid { get; private set; } + public Guid GuidAaguid { get { return FromBigEndian(Aaguid); } } public byte[] Aaguid { get; private set; } public byte[] CredentialID { get; private set; } public byte[] CredentialPublicKey { get; private set; } diff --git a/fido2-net-lib/CryptoUtils.cs b/fido2-net-lib/CryptoUtils.cs index 981eabda..70799548 100644 --- a/fido2-net-lib/CryptoUtils.cs +++ b/fido2-net-lib/CryptoUtils.cs @@ -59,12 +59,7 @@ public static HashAlgorithm GetHasher(HashAlgorithmName hashName) { "1.2.840.10045.2.1", 2 }, // ECC { "1.2.840.113549.1.1.1", 3} // RSA }; - public static readonly Dictionary CoseCurveToTpm = new Dictionary - { - { 1, TpmEccCurve.TPM_ECC_NIST_P256}, - { 2, TpmEccCurve.TPM_ECC_NIST_P384}, - { 3, TpmEccCurve.TPM_ECC_NIST_P521} - }; + public static CBORObject CoseKeyFromCertAndAlg(X509Certificate2 cert, int alg) { var coseKey = CBORObject.NewMap(); @@ -151,6 +146,10 @@ public static bool VerifySigWithCoseKey(byte[] data, CBORObject coseKey, byte[] case 3: curve = ECCurve.NamedCurves.nistP521; System.Diagnostics.Debug.WriteLine(BitConverter.ToString(sig).Replace("-", "")); + System.Diagnostics.Debug.WriteLine(BitConverter.ToString(sig)); + System.Diagnostics.Debug.WriteLine(BitConverter.ToString(point.X)); + System.Diagnostics.Debug.WriteLine(BitConverter.ToString(point.Y)); + System.Diagnostics.Debug.WriteLine(BitConverter.ToString(data)); break; default: throw new ArgumentOutOfRangeException("crv"); @@ -170,7 +169,7 @@ public static bool VerifySigWithCoseKey(byte[] data, CBORObject coseKey, byte[] } case 3: // RSA { - RSACng rsa = new RSACng(); + var rsa = new RSACng(); rsa.ImportParameters( new RSAParameters() { diff --git a/fido2-net-lib/TPM.cs b/fido2-net-lib/TPM.cs deleted file mode 100644 index 549e9d2e..00000000 --- a/fido2-net-lib/TPM.cs +++ /dev/null @@ -1,241 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; - -namespace Fido2NetLib -{ - public enum TpmEccCurve : UInt16 - { - // TCG TPM Rev 2.0, part 2, structures, section 6.4, TPM_ECC_CURVE - TPM_ECC_NONE, // 0x0000 - TPM_ECC_NIST_P192, // 0x0001 - TPM_ECC_NIST_P224, // 0x0002 - TPM_ECC_NIST_P256, // 0x0003 - TPM_ECC_NIST_P384, // 0x0004 - TPM_ECC_NIST_P521, // 0x0005 - TPM_ECC_BN_P256, // 0x0010 curve to support ECDAA - TPM_ECC_BN_P638, // 0x0011 curve to support ECDAA - TPM_ECC_SM2_P256 // 0x0020 - } - public enum TpmAlg : UInt16 - { - // TCG TPM Rev 2.0, part 2, structures, section 6.3, TPM_ALG_ID - TPM_ALG_ERROR, // 0 - TPM_ALG_RSA, // 1 - TPM_ALG_SHA1 = 4, // 4 - TPM_ALG_HMAC, // 5 - TPM_ALG_AES, // 6 - TPM_ALG_MGF1, // 7 - TPM_ALG_KEYEDHASH, // 8 - TPM_ALG_XOR = 0xA, // A - TPM_ALG_SHA256, // B - TPM_ALG_SHA384, // C - TPM_ALG_SHA512, // D - TPM_ALG_NULL = 0x10, // 10 - TPM_ALG_SM3_256 = 0x12, // 12 - TPM_ALG_SM4, // 13 - TPM_ALG_RSASSA, // 14 - TPM_ALG_RSAES, // 15 - TPM_ALG_RSAPSS, // 16 - TPM_ALG_OAEP, // 17 - TPM_ALG_ECDSA, // 18 - TPM_ALG_ECDH, // 19 - TPM_ALG_ECDAA, // 1A - TPM_ALG_SM2, // 1B - TPM_ALG_ECSCHNORR, // 1C - TPM_ALG_ECMQV, // 1D - TPM_ALG_KDF1_SP800_56A = 0x20, - TPM_ALG_KDF2, // 21 - TPM_ALG_KDF1_SP800_108, // 22 - TPM_ALG_ECC, // 23 - TPM_ALG_SYMCIPHER = 0x25, - TPM_ALG_CAMELLIA, // 26 - TPM_ALG_CTR = 0x40, - TPM_ALG_OFB, // 41 - TPM_ALG_CBC, // 42 - TPM_ALG_CFB, // 43 - TPM_ALG_ECB // 44 - }; - // TPMS_ATTEST, TPMv2-Part2, section 10.12.8 - public class CertInfo - { - public static readonly Dictionary tpmAlgToDigestSizeMap = new Dictionary - { - {TpmAlg.TPM_ALG_SHA1, (160/8) }, - {TpmAlg.TPM_ALG_SHA256, (256/8) }, - {TpmAlg.TPM_ALG_SHA384, (384/8) }, - {TpmAlg.TPM_ALG_SHA512, (512/8) } - }; - public static (UInt16 size, byte[] name) NameFromTPM2BName(Memory ab, ref int offset) - { - // TCG TPM Rev 2.0, part 2, structures, section 10.5.3, TPM2B_NAME - // This buffer holds a Name for any entity type. - // The type of Name in the structure is determined by context and the size parameter. - var totalBytes = AuthDataHelper.GetSizedByteArray(ab, ref offset, 2); - UInt16 totalSize = 0; - if (null != totalBytes) - { - totalSize = BitConverter.ToUInt16(totalBytes.ToArray().Reverse().ToArray(), 0); - } - UInt16 size = 0; - var bytes = AuthDataHelper.GetSizedByteArray(ab, ref offset, 2); - if (null != bytes) - { - size = BitConverter.ToUInt16(bytes.ToArray().Reverse().ToArray(), 0); - } - // If size is four, then the Name is a handle. - if (4 == size) throw new Fido2VerificationException("Unexpected handle in TPM2B_NAME"); - // If size is zero, then no Name is present. - if (0 == size) throw new Fido2VerificationException("Unexpected no name found in TPM2B_NAME"); - // Otherwise, the size shall be the size of a TPM_ALG_ID plus the size of the digest produced by the indicated hash algorithm. - TpmAlg tpmalg = TpmAlg.TPM_ALG_ERROR; - byte[] name = null; - if (Enum.IsDefined(typeof(TpmAlg), size)) - { - tpmalg = (TpmAlg)size; - if (tpmAlgToDigestSizeMap.ContainsKey(tpmalg)) - { - name = AuthDataHelper.GetSizedByteArray(ab, ref offset, tpmAlgToDigestSizeMap[tpmalg]); - } - } - if (totalSize != bytes.Length + name.Length) throw new Fido2VerificationException("Unexpected no name found in TPM2B_NAME"); - return (size, name); - } - public CertInfo(byte[] certInfo) - { - if (null == certInfo || 0 == certInfo.Length) throw new Fido2VerificationException("Malformed certInfo bytes"); - Raw = certInfo; - var offset = 0; - Magic = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); - if (0xff544347 != BitConverter.ToUInt32(Magic.ToArray().Reverse().ToArray(), 0)) throw new Fido2VerificationException("Bad magic number " + Magic.ToString()); - Type = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 2); - if (0x8017 != BitConverter.ToUInt16(Type.ToArray().Reverse().ToArray(), 0)) throw new Fido2VerificationException("Bad structure tag " + Type.ToString()); - QualifiedSigner = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); - ExtraData = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); - if (null == ExtraData || 0 == ExtraData.Length) throw new Fido2VerificationException("Bad extraData in certInfo"); - Clock = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 8); - ResetCount = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); - RestartCount = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); - Safe = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 1); - FirmwareVersion = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 8); - var TPM2BName = NameFromTPM2BName(certInfo, ref offset); - Alg = TPM2BName.size; // TPM_ALG_ID - AttestedName = TPM2BName.name; - AttestedQualifiedNameBuffer = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); - if (certInfo.Length != offset) throw new Fido2VerificationException("Leftover bits decoding certInfo"); - } - public byte[] Raw { get; private set; } - public byte[] Magic { get; private set; } - public byte[] Type { get; private set; } - public byte[] QualifiedSigner { get; private set; } - public byte[] ExtraData { get; private set; } - public byte[] Clock { get; private set; } - public byte[] ResetCount { get; private set; } - public byte[] RestartCount { get; private set; } - public byte[] Safe { get; private set; } - public byte[] FirmwareVersion { get; private set; } - public UInt16 Alg { get; private set; } - public byte[] AttestedName { get; private set; } - public byte[] AttestedQualifiedNameBuffer { get; private set; } - } - // TPMT_PUBLIC, TPMv2-Part2, section 12.2.4 - public class PubArea - { - public PubArea(byte[] pubArea) - { - Raw = pubArea; - var offset = 0; - - // TPMI_ALG_PUBLIC - Type = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - var tpmalg = (TpmAlg)Enum.Parse(typeof(TpmAlg), BitConverter.ToUInt16(Type.ToArray().Reverse().ToArray(), 0).ToString()); - - // TPMI_ALG_HASH - Alg = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - - // TPMA_OBJECT, attributes that, along with type, determine the manipulations of this object - Attributes = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 4); - - // TPM2B_DIGEST, optional policy for using this key, computed using the alg of the object - Policy = AuthDataHelper.GetSizedByteArray(pubArea, ref offset); - - // TPMU_PUBLIC_PARMS - Symmetric = null; - Scheme = null; - - if (TpmAlg.TPM_ALG_KEYEDHASH == tpmalg) - { - throw new Fido2VerificationException("TPM_ALG_KEYEDHASH not yet supported"); - } - if (TpmAlg.TPM_ALG_SYMCIPHER == tpmalg) - { - throw new Fido2VerificationException("TPM_ALG_SYMCIPHER not yet supported"); - } - - // TPMS_ASYM_PARMS, for TPM_ALG_RSA and TPM_ALG_ECC - if (TpmAlg.TPM_ALG_RSA == tpmalg || TpmAlg.TPM_ALG_ECC == tpmalg) - { - Symmetric = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - Scheme = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - } - - // TPMI_RSA_KEY_BITS, number of bits in the public modulus - KeyBits = null; - // The public exponent, a prime number greater than 2. When zero, indicates that the exponent is the default of 2^16 + 1 - Exponent = 0; - - if (TpmAlg.TPM_ALG_RSA == tpmalg) - { - KeyBits = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - var tmp = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 4); - if (null != tmp) - { - Exponent = BitConverter.ToUInt32(tmp.ToArray(), 0); - if (0 == Exponent) Exponent = System.Convert.ToUInt32(Math.Pow(2, 16) + 1); - } - } - - // TPMI_ECC_CURVE - CurveID = null; - - // TPMT_KDF_SCHEME, an optional key derivation scheme for generating a symmetric key from a Z value - // If the kdf parameter associated with curveID is not TPM_ALG_NULL then this is required to be NULL. - // NOTE There are currently no commands where this parameter has effect and, in the reference code, this field needs to be set to TPM_ALG_NULL. - KDF = null; - - if (TpmAlg.TPM_ALG_ECC == tpmalg) - { - CurveID = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - KDF = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); - } - - // TPMU_PUBLIC_ID - Unique = AuthDataHelper.GetSizedByteArray(pubArea, ref offset); - if (pubArea.Length != offset) throw new Fido2VerificationException("Leftover bytes decoding pubArea"); - if (null != CurveID) - { - var point = new System.Security.Cryptography.ECPoint(); - var uniqueOffset = 0; - var size = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, 2); - point.X = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, BitConverter.ToUInt16(size.Reverse().ToArray(), 0)); - size = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, 2); - point.Y = AuthDataHelper.GetSizedByteArray(Unique, ref uniqueOffset, BitConverter.ToUInt16(size.Reverse().ToArray(), 0)); - ECPoint = point; - } - } - public byte[] Raw { get; private set; } - public byte[] Type { get; private set; } - public byte[] Alg { get; private set; } - public byte[] Attributes { get; private set; } - public byte[] Policy { get; private set; } - public byte[] Symmetric { get; private set; } - public byte[] Scheme { get; private set; } - public byte[] KeyBits { get; private set; } - public UInt32 Exponent { get; private set; } - public byte[] CurveID { get; private set; } - public TpmEccCurve EccCurve { get; private set; } - public byte[] KDF { get; private set; } - public byte[] Unique { get; private set; } - public System.Security.Cryptography.ECPoint ECPoint { get; private set; } - } -} From d05b899321f70eec9df05e8aaa7c29eae2971fff Mon Sep 17 00:00:00 2001 From: Alex Seigler Date: Fri, 26 Oct 2018 10:34:44 -0400 Subject: [PATCH 10/10] Convert Chaos.NaCl to netstandard1.0 --- Chaos.NaCl/Chaos.NaCl-Portable.csproj | 111 ------------------- Chaos.NaCl/Chaos.NaCl.csproj | 126 +++------------------- Chaos.NaCl/Properties/AssemblyInfo.cs | 30 ------ Chaos.NaCl/Properties/AssemblyInfoFull.cs | 9 -- fido2-net-lib.sln | 12 +-- 5 files changed, 19 insertions(+), 269 deletions(-) delete mode 100644 Chaos.NaCl/Chaos.NaCl-Portable.csproj delete mode 100644 Chaos.NaCl/Properties/AssemblyInfo.cs delete mode 100644 Chaos.NaCl/Properties/AssemblyInfoFull.cs diff --git a/Chaos.NaCl/Chaos.NaCl-Portable.csproj b/Chaos.NaCl/Chaos.NaCl-Portable.csproj deleted file mode 100644 index a929c239..00000000 --- a/Chaos.NaCl/Chaos.NaCl-Portable.csproj +++ /dev/null @@ -1,111 +0,0 @@ - - - - - 10.0 - Debug - AnyCPU - {992710C3-DFC9-4CC1-A38B-B3F7E4A41B1C} - Library - Properties - Chaos.NaCl - Chaos.NaCl-Portable - v4.0 - Profile1 - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Chaos.NaCl/Chaos.NaCl.csproj b/Chaos.NaCl/Chaos.NaCl.csproj index 65b0a5f9..1b24d985 100644 --- a/Chaos.NaCl/Chaos.NaCl.csproj +++ b/Chaos.NaCl/Chaos.NaCl.csproj @@ -1,119 +1,19 @@ - - - + + - Debug - AnyCPU - {AE28FD14-7985-4707-A963-C94B8597AE50} - Library - Properties - Chaos.NaCl - Chaos.NaCl - v4.6.1 - 512 - - + netstandard1.0 + + + false + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - SecurityRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - default - MinimumRecommendedRules.ruleset - false - - - false + + + Off + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + diff --git a/Chaos.NaCl/Properties/AssemblyInfo.cs b/Chaos.NaCl/Properties/AssemblyInfo.cs deleted file mode 100644 index a8650f94..00000000 --- a/Chaos.NaCl/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Chaos.NaCl")] -[assembly: AssemblyDescription("C# port of the NaCl cryptography library")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("CodesInChaos")] -[assembly: AssemblyProduct("Chaos.NaCl cryptography library")] -[assembly: AssemblyCopyright("public domain")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] - -[assembly: InternalsVisibleTo("Chaos.NaCl.Tests")] -[assembly: InternalsVisibleTo("Chaos.NaCl.Benchmark")] \ No newline at end of file diff --git a/Chaos.NaCl/Properties/AssemblyInfoFull.cs b/Chaos.NaCl/Properties/AssemblyInfoFull.cs deleted file mode 100644 index 80fd16c1..00000000 --- a/Chaos.NaCl/Properties/AssemblyInfoFull.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Runtime.InteropServices; - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f07e7dd1-d31c-4994-8948-e42de7ef16ec")] \ No newline at end of file diff --git a/fido2-net-lib.sln b/fido2-net-lib.sln index b68d3dea..50edd17a 100644 --- a/fido2-net-lib.sln +++ b/fido2-net-lib.sln @@ -14,10 +14,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaos.NaCl", "Chaos.NaCl\Chaos.NaCl.csproj", "{AE28FD14-7985-4707-A963-C94B8597AE50}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CBOR", "CBOR\CBOR.csproj", "{913DE613-DE3A-402D-9EF6-722ED255BF6A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaos.NaCl", "Chaos.NaCl\Chaos.NaCl.csproj", "{B3A138EB-6EDA-4D50-B2BF-E080AB4D74E9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,14 +36,14 @@ Global {06E82E3F-C626-4070-98BB-6D307DA86AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {06E82E3F-C626-4070-98BB-6D307DA86AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {06E82E3F-C626-4070-98BB-6D307DA86AC2}.Release|Any CPU.Build.0 = Release|Any CPU - {AE28FD14-7985-4707-A963-C94B8597AE50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE28FD14-7985-4707-A963-C94B8597AE50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE28FD14-7985-4707-A963-C94B8597AE50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE28FD14-7985-4707-A963-C94B8597AE50}.Release|Any CPU.Build.0 = Release|Any CPU {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Debug|Any CPU.Build.0 = Debug|Any CPU {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {913DE613-DE3A-402D-9EF6-722ED255BF6A}.Release|Any CPU.Build.0 = Release|Any CPU + {B3A138EB-6EDA-4D50-B2BF-E080AB4D74E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3A138EB-6EDA-4D50-B2BF-E080AB4D74E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3A138EB-6EDA-4D50-B2BF-E080AB4D74E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3A138EB-6EDA-4D50-B2BF-E080AB4D74E9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE