@@ -15,6 +15,9 @@ import (
15
15
// https://www.secg.org/sec2-v2.pdf
16
16
//
17
17
// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)
18
+ //
19
+ // [BRID]: On Binary Representations of Integers with Digits -1, 0, 1
20
+ // (Prodinger, Helmut)
18
21
19
22
// All group operations are performed using Jacobian coordinates. For a given
20
23
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
@@ -636,6 +639,145 @@ func splitK(k []byte) ([]byte, []byte, int, int) {
636
639
return k1 .Bytes (), k2 .Bytes (), k1 .Sign (), k2 .Sign ()
637
640
}
638
641
642
+ // nafScalar represents a positive integer up to a maximum value of 2^256 - 1
643
+ // encoded in non-adjacent form.
644
+ //
645
+ // NAF is a signed-digit representation where each digit can be +1, 0, or -1.
646
+ //
647
+ // In order to efficiently encode that information, this type uses two arrays, a
648
+ // "positive" array where set bits represent the +1 signed digits and a
649
+ // "negative" array where set bits represent the -1 signed digits. 0 is
650
+ // represented by neither array having a bit set in that position.
651
+ //
652
+ // The Pos and Neg methods return the aforementioned positive and negative
653
+ // arrays, respectively.
654
+ type nafScalar struct {
655
+ // pos houses the positive portion of the representation. An additional
656
+ // byte is required for the positive portion because the NAF encoding can be
657
+ // up to 1 bit longer than the normal binary encoding of the value.
658
+ //
659
+ // neg houses the negative portion of the representation. Even though the
660
+ // additional byte is not required for the negative portion, since it can
661
+ // never exceed the length of the normal binary encoding of the value,
662
+ // keeping the same length for positive and negative portions simplifies
663
+ // working with the representation and allows extra conditional branches to
664
+ // be avoided.
665
+ //
666
+ // start and end specify the starting and ending index to use within the pos
667
+ // and neg arrays, respectively. This allows fixed size arrays to be used
668
+ // versus needing to dynamically allocate space on the heap.
669
+ //
670
+ // NOTE: The fields are defined in the order that they are to minimize the
671
+ // padding on 32-bit and 64-bit platforms.
672
+ pos [33 ]byte
673
+ start , end uint8
674
+ neg [33 ]byte
675
+ }
676
+
677
+ // Pos returns the bytes of the encoded value with bits set in the positions
678
+ // that represent a signed digit of +1.
679
+ func (s * nafScalar ) Pos () []byte {
680
+ return s .pos [s .start :s .end ]
681
+ }
682
+
683
+ // Neg returns the bytes of the encoded value with bits set in the positions
684
+ // that represent a signed digit of -1.
685
+ func (s * nafScalar ) Neg () []byte {
686
+ return s .neg [s .start :s .end ]
687
+ }
688
+
689
+ // naf takes a positive integer up to a maximum value of 2^256 - 1 and returns
690
+ // its non-adjacent form (NAF), which is a unique signed-digit representation
691
+ // such that no two consecutive digits are nonzero. See the documentation for
692
+ // the returned type for details on how the representation is encoded
693
+ // efficiently and how to interpret it
694
+ //
695
+ // NAF is useful in that it has the fewest nonzero digits of any signed digit
696
+ // representation, only 1/3rd of its digits are nonzero on average, and at least
697
+ // half of the digits will be 0.
698
+ //
699
+ // The aforementioned properties are particularly beneficial for optimizing
700
+ // elliptic curve point multiplication because they effectively minimize the
701
+ // number of required point additions in exchange for needing to perform a mix
702
+ // of fewer point additions and subtractions and possibly one additional point
703
+ // doubling. This is an excellent tradeoff because subtraction of points has
704
+ // the same computational complexity as addition of points and point doubling is
705
+ // faster than both.
706
+ func naf (k []byte ) nafScalar {
707
+ // Strip leading zero bytes.
708
+ for len (k ) > 0 && k [0 ] == 0x00 {
709
+ k = k [1 :]
710
+ }
711
+
712
+ // The non-adjacent form (NAF) of a positive integer k is an expression
713
+ // k = ∑_(i=0, l-1) k_i * 2^i where k_i ∈ {0,±1}, k_(l-1) != 0, and no two
714
+ // consecutive digits k_i are nonzero.
715
+ //
716
+ // The traditional method of computing the NAF of a positive integer is
717
+ // given by algorithm 3.30 in [GECC]. It consists of repeatedly dividing k
718
+ // by 2 and choosing the remainder so that the quotient (k−r)/2 is even
719
+ // which ensures the next NAF digit is 0. This requires log_2(k) steps.
720
+ //
721
+ // However, in [BRID], Prodinger notes that a closed form expression for the
722
+ // NAF representation is the bitwise difference 3k/2 - k/2. This is more
723
+ // efficient as it can be computed in O(1) versus the O(log(n)) of the
724
+ // traditional approach.
725
+ //
726
+ // The following code makes use of that formula to compute the NAF more
727
+ // efficiently.
728
+ //
729
+ // To understand the logic here, observe that the only way the NAF has a
730
+ // nonzero digit at a given bit is when either 3k/2 or k/2 has a bit set in
731
+ // that position, but not both. In other words, the result of a bitwise
732
+ // xor. This can be seen simply by considering that when the bits are the
733
+ // same, the subtraction is either 0-0 or 1-1, both of which are 0.
734
+ //
735
+ // Further, observe that the "1" digits in the result are contributed by
736
+ // 3k/2 while the "-1" digits are from k/2. So, they can be determined
737
+ // by taking the bitwise and of each respective value with the result of
738
+ // the xor which identifies which bits are nonzero.
739
+ //
740
+ // Using that information, this loops backwards from the least significant
741
+ // byte to the most significant byte while performing the aforementioned
742
+ // calculations by propagating the potential carry and high order bit from
743
+ // the next word during the right shift.
744
+ kLen := len (k )
745
+ var result nafScalar
746
+ var carry uint8
747
+ for byteNum := kLen - 1 ; byteNum >= 0 ; byteNum -- {
748
+ // Calculate x/2. Notice the low order bit from the next word is
749
+ // shifted in accordingly.
750
+ x := uint16 (k [byteNum ]) + uint16 (carry )
751
+ var nextWord uint8
752
+ if byteNum > 0 {
753
+ nextWord = k [byteNum - 1 ]
754
+ }
755
+ halfX := x >> 1 | uint16 (nextWord << 7 )
756
+
757
+ // Calculate 3x/2 and determine the non-zero digits in the result.
758
+ threeHalfX := x + halfX
759
+ nonZeroResultDigits := threeHalfX ^ halfX
760
+
761
+ // Determine the signed digits {0, ±1}.
762
+ xPositive := threeHalfX & nonZeroResultDigits
763
+ xNegative := halfX & nonZeroResultDigits
764
+
765
+ // Propagate the potential carry from the 3x/2 calculation.
766
+ carry = uint8 (threeHalfX >> 8 )
767
+
768
+ result .pos [byteNum + 1 ] = uint8 (xPositive )
769
+ result .neg [byteNum + 1 ] = uint8 (xNegative )
770
+ }
771
+ result .pos [0 ] = carry
772
+
773
+ // Set the starting and ending positions within the fixed size arrays to
774
+ // identify the bytes that are actually used. This is important since the
775
+ // encoding is big endian and thus trailing zero bytes changes its value.
776
+ result .start = 1 - carry
777
+ result .end = uint8 (kLen + 1 )
778
+ return result
779
+ }
780
+
639
781
// ScalarMultNonConst multiplies k*P where k is a big endian integer modulo the
640
782
// curve order and P is a point in Jacobian projective coordinates and stores
641
783
// the result in the provided Jacobian point.
@@ -683,8 +825,9 @@ func ScalarMultNonConst(k *ModNScalar, point, result *JacobianPoint) {
683
825
//
684
826
// The Pos version of the bytes contain the +1s and the Neg versions
685
827
// contain the -1s.
686
- k1PosNAF , k1NegNAF := naf (k1 )
687
- k2PosNAF , k2NegNAF := naf (k2 )
828
+ k1NAF , k2NAF := naf (k1 ), naf (k2 )
829
+ k1PosNAF , k1NegNAF := k1NAF .Pos (), k1NAF .Neg ()
830
+ k2PosNAF , k2NegNAF := k2NAF .Pos (), k2NAF .Neg ()
688
831
k1Len , k2Len := len (k1PosNAF ), len (k2PosNAF )
689
832
690
833
m := k1Len
@@ -770,88 +913,6 @@ func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) {
770
913
result .Set (& q )
771
914
}
772
915
773
- // naf takes a positive integer k and returns the Non-Adjacent Form (NAF) as two
774
- // byte slices. The first is where 1s will be. The second is where -1s will
775
- // be. NAF is convenient in that on average, only 1/3rd of its values are
776
- // non-zero. This is algorithm 3.30 from [GECC].
777
- //
778
- // Essentially, this makes it possible to minimize the number of operations
779
- // since the resulting ints returned will be at least 50% 0s.
780
- func naf (k []byte ) ([]byte , []byte ) {
781
- // Strip leading zero bytes.
782
- for len (k ) > 0 && k [0 ] == 0x00 {
783
- k = k [1 :]
784
- }
785
-
786
- // The essence of this algorithm is that whenever we have consecutive 1s
787
- // in the binary, we want to put a -1 in the lowest bit and get a bunch
788
- // of 0s up to the highest bit of consecutive 1s. This is due to this
789
- // identity:
790
- // 2^n + 2^(n-1) + 2^(n-2) + ... + 2^(n-k) = 2^(n+1) - 2^(n-k)
791
- //
792
- // The algorithm thus may need to go 1 more bit than the length of the
793
- // bits we actually have, hence bits being 1 bit longer than was
794
- // necessary. Since we need to know whether adding will cause a carry,
795
- // we go from right-to-left in this addition.
796
- var carry , curIsOne , nextIsOne bool
797
- // these default to zero
798
- retPos := make ([]byte , len (k )+ 1 )
799
- retNeg := make ([]byte , len (k )+ 1 )
800
- for i := len (k ) - 1 ; i >= 0 ; i -- {
801
- curByte := k [i ]
802
- for j := uint (0 ); j < 8 ; j ++ {
803
- curIsOne = curByte & 1 == 1
804
- if j == 7 {
805
- if i == 0 {
806
- nextIsOne = false
807
- } else {
808
- nextIsOne = k [i - 1 ]& 1 == 1
809
- }
810
- } else {
811
- nextIsOne = curByte & 2 == 2
812
- }
813
- if carry {
814
- if curIsOne {
815
- // This bit is 1, so continue to carry
816
- // and don't need to do anything.
817
- } else {
818
- // We've hit a 0 after some number of
819
- // 1s.
820
- if nextIsOne {
821
- // Start carrying again since
822
- // a new sequence of 1s is
823
- // starting.
824
- retNeg [i + 1 ] += 1 << j
825
- } else {
826
- // Stop carrying since 1s have
827
- // stopped.
828
- carry = false
829
- retPos [i + 1 ] += 1 << j
830
- }
831
- }
832
- } else if curIsOne {
833
- if nextIsOne {
834
- // If this is the start of at least 2
835
- // consecutive 1s, set the current one
836
- // to -1 and start carrying.
837
- retNeg [i + 1 ] += 1 << j
838
- carry = true
839
- } else {
840
- // This is a singleton, not consecutive
841
- // 1s.
842
- retPos [i + 1 ] += 1 << j
843
- }
844
- }
845
- curByte >>= 1
846
- }
847
- }
848
- if carry {
849
- retPos [0 ] = 1
850
- return retPos , retNeg
851
- }
852
- return retPos [1 :], retNeg [1 :]
853
- }
854
-
855
916
// isOnCurve returns whether or not the affine point (x,y) is on the curve.
856
917
func isOnCurve (fx , fy * FieldVal ) bool {
857
918
// Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7
0 commit comments