@@ -78,6 +78,147 @@ bool ApplyRSAOptions(const EVPKeyPointer& pkey,
7878 return true ;
7979}
8080
81+ constexpr size_t kEd25519PointSize = 32 ;
82+ constexpr size_t kEd448PointSize = 57 ;
83+
84+ // Ed25519 has cofactor 8, so the first eight entries are the full
85+ // canonical small-order subgroup: identity, one point of order 2,
86+ // two points of order 4, and four points of order 8.
87+ constexpr unsigned char kEd25519SmallOrderPoints [][kEd25519PointSize ] = {
88+ // Identity.
89+ {0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
90+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
91+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 },
92+ // Order 2.
93+ {0xec , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
94+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
95+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x7f },
96+ // Order 4.
97+ {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
98+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
99+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 },
100+ {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
101+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
102+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 },
103+ // Order 8.
104+ {0xc7 , 0x17 , 0x6a , 0x70 , 0x3d , 0x4d , 0xd8 , 0x4f , 0xba , 0x3c , 0x0b ,
105+ 0x76 , 0x0d , 0x10 , 0x67 , 0x0f , 0x2a , 0x20 , 0x53 , 0xfa , 0x2c , 0x39 ,
106+ 0xcc , 0xc6 , 0x4e , 0xc7 , 0xfd , 0x77 , 0x92 , 0xac , 0x03 , 0x7a },
107+ {0xc7 , 0x17 , 0x6a , 0x70 , 0x3d , 0x4d , 0xd8 , 0x4f , 0xba , 0x3c , 0x0b ,
108+ 0x76 , 0x0d , 0x10 , 0x67 , 0x0f , 0x2a , 0x20 , 0x53 , 0xfa , 0x2c , 0x39 ,
109+ 0xcc , 0xc6 , 0x4e , 0xc7 , 0xfd , 0x77 , 0x92 , 0xac , 0x03 , 0xfa },
110+ {0x26 , 0xe8 , 0x95 , 0x8f , 0xc2 , 0xb2 , 0x27 , 0xb0 , 0x45 , 0xc3 , 0xf4 ,
111+ 0x89 , 0xf2 , 0xef , 0x98 , 0xf0 , 0xd5 , 0xdf , 0xac , 0x05 , 0xd3 , 0xc6 ,
112+ 0x33 , 0x39 , 0xb1 , 0x38 , 0x02 , 0x88 , 0x6d , 0x53 , 0xfc , 0x05 },
113+ {0x26 , 0xe8 , 0x95 , 0x8f , 0xc2 , 0xb2 , 0x27 , 0xb0 , 0x45 , 0xc3 , 0xf4 ,
114+ 0x89 , 0xf2 , 0xef , 0x98 , 0xf0 , 0xd5 , 0xdf , 0xac , 0x05 , 0xd3 , 0xc6 ,
115+ 0x33 , 0x39 , 0xb1 , 0x38 , 0x02 , 0x88 , 0x6d , 0x53 , 0xfc , 0x85 },
116+ // Non-canonical encodings of the same small-order points.
117+ {0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
118+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
119+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 },
120+ {0xec , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
121+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
122+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff },
123+ {0xee , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
124+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
125+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x7f },
126+ {0xee , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
127+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
128+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff },
129+ {0xed , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
130+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
131+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff },
132+ {0xed , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
133+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
134+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x7f },
135+ };
136+
137+ // Ed448 has cofactor 4, so these four entries are the full canonical
138+ // small-order subgroup: identity, one point of order 2, and two points
139+ // of order 4.
140+ constexpr unsigned char kEd448SmallOrderPoints [][kEd448PointSize ] = {
141+ // Identity.
142+ {0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
143+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
144+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
145+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
146+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 },
147+ // Order 2.
148+ {0xfe , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
149+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
150+ 0xff , 0xff , 0xff , 0xff , 0xfe , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
151+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
152+ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 },
153+ // Order 4.
154+ {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
155+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
156+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
157+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
158+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 },
159+ {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
160+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
161+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
162+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
163+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 },
164+ };
165+
166+ template <size_t PointSize, size_t Count>
167+ bool ContainsPoint (const unsigned char * candidate,
168+ const unsigned char (&points)[Count][PointSize]) {
169+ for (const auto & point : points) {
170+ if (memcmp (candidate, point, PointSize) == 0 ) return true ;
171+ }
172+ return false ;
173+ }
174+
175+ bool IsSmallOrderEdDsaPoint (int id,
176+ const unsigned char * candidate,
177+ size_t size) {
178+ switch (id) {
179+ case EVP_PKEY_ED25519 :
180+ return size == kEd25519PointSize &&
181+ ContainsPoint (candidate, kEd25519SmallOrderPoints );
182+ case EVP_PKEY_ED448 :
183+ return size == kEd448PointSize &&
184+ ContainsPoint (candidate, kEd448SmallOrderPoints );
185+ default :
186+ return false ;
187+ }
188+ }
189+
190+ bool HasSmallOrderEdDsaPoint (const EVPKeyPointer& key,
191+ const ByteSource& signature) {
192+ const int id = key.id ();
193+ size_t point_size;
194+
195+ switch (id) {
196+ case EVP_PKEY_ED25519 :
197+ point_size = kEd25519PointSize ;
198+ break ;
199+ case EVP_PKEY_ED448 :
200+ point_size = kEd448PointSize ;
201+ break ;
202+ default :
203+ return false ;
204+ }
205+
206+ if (signature.size () != point_size * 2 ) return false ;
207+
208+ if (IsSmallOrderEdDsaPoint (id, signature.data <unsigned char >(), point_size)) {
209+ return true ;
210+ }
211+
212+ unsigned char raw_public_key[kEd448PointSize ];
213+ size_t raw_public_key_size = point_size;
214+ if (EVP_PKEY_get_raw_public_key (
215+ key.get (), raw_public_key, &raw_public_key_size) != 1 ) {
216+ return false ;
217+ }
218+
219+ return IsSmallOrderEdDsaPoint (id, raw_public_key, raw_public_key_size);
220+ }
221+
81222std::unique_ptr<BackingStore> Node_SignFinal (Environment* env,
82223 EVPMDCtxPointer&& mdctx,
83224 const EVPKeyPointer& pkey,
@@ -754,7 +895,8 @@ bool SignTraits::DeriveBits(Environment* env,
754895 case SignConfiguration::Mode::Verify: {
755896 auto buf = DataPointer::Alloc (1 );
756897 static_cast <char *>(buf.get ())[0 ] = 0 ;
757- if (context.verify (params.data , params.signature )) {
898+ if (context.verify (params.data , params.signature ) &&
899+ !HasSmallOrderEdDsaPoint (key, params.signature )) {
758900 static_cast <char *>(buf.get ())[0 ] = 1 ;
759901 }
760902 *out = ByteSource::Allocated (buf.release ());
0 commit comments