Skip to content

Commit 51dc34e

Browse files
Add helper to get padded ec signature (#701)
1 parent ccb13f8 commit 51dc34e

File tree

6 files changed

+61
-2
lines changed

6 files changed

+61
-2
lines changed

awscrt/crypto.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,16 @@ def decode_der_signature(signature: bytes) -> ECRawSignature:
286286
(r, s) = _awscrt.ec_decode_signature(signature)
287287
return ECRawSignature(r=r, s=s)
288288

289+
@staticmethod
290+
def decode_der_signature_to_padded_pair(signature: bytes, pad_to: int) -> bytes:
291+
"""
292+
Decodes ec signature into padded r || s.
293+
Both r and s are padded to the specified value and then concatenated.
294+
Typical format for jwt use case.
295+
Note: pad_to typically should equal to coord size. e.g 32 for P256 or 48 for P384
296+
"""
297+
return _awscrt.ec_decode_signature_to_padded_pair(signature, pad_to)
298+
289299
@staticmethod
290300
def encode_raw_signature(signature: ECRawSignature) -> bytes:
291301
"""

source/crypto.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,35 @@ PyObject *aws_py_ec_decode_signature(PyObject *self, PyObject *args) {
859859
return result;
860860
}
861861

862+
PyObject *aws_py_ec_decode_signature_to_padded_pair(PyObject *self, PyObject *args) {
863+
(void)self;
864+
865+
Py_ssize_t pad_to;
866+
struct aws_byte_cursor signature_cur;
867+
if (!PyArg_ParseTuple(args, "y#n", &signature_cur.ptr, &signature_cur.len, &pad_to)) {
868+
return NULL;
869+
}
870+
871+
if (pad_to > 256) {
872+
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
873+
return PyErr_AwsLastError();
874+
}
875+
876+
struct aws_allocator *allocator = aws_py_get_allocator();
877+
size_t buf_size = pad_to * 2;
878+
struct aws_byte_buf result_buf;
879+
aws_byte_buf_init(&result_buf, allocator, buf_size);
880+
881+
if (aws_ecc_decode_signature_der_to_raw_padded(allocator, signature_cur, &result_buf, pad_to)) {
882+
aws_byte_buf_clean_up_secure(&result_buf);
883+
return PyErr_AwsLastError();
884+
}
885+
886+
PyObject *ret = PyBytes_FromStringAndSize((const char *)result_buf.buffer, result_buf.len);
887+
aws_byte_buf_clean_up_secure(&result_buf);
888+
return ret;
889+
}
890+
862891
PyObject *aws_py_ec_get_public_coords(PyObject *self, PyObject *args) {
863892
(void)self;
864893

source/crypto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ PyObject *aws_py_ec_sign(PyObject *self, PyObject *args);
5353
PyObject *aws_py_ec_verify(PyObject *self, PyObject *args);
5454
PyObject *aws_py_ec_encode_signature(PyObject *self, PyObject *args);
5555
PyObject *aws_py_ec_decode_signature(PyObject *self, PyObject *args);
56+
PyObject *aws_py_ec_decode_signature_to_padded_pair(PyObject *self, PyObject *args);
5657
PyObject *aws_py_ec_get_public_coords(PyObject *self, PyObject *args);
5758

5859
#endif /* AWS_CRT_PYTHON_CRYPTO_H */

source/module.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ static PyMethodDef s_module_methods[] = {
852852
AWS_PY_METHOD_DEF(ec_verify, METH_VARARGS),
853853
AWS_PY_METHOD_DEF(ec_encode_signature, METH_VARARGS),
854854
AWS_PY_METHOD_DEF(ec_decode_signature, METH_VARARGS),
855+
AWS_PY_METHOD_DEF(ec_decode_signature_to_padded_pair, METH_VARARGS),
855856
AWS_PY_METHOD_DEF(ec_get_public_coords, METH_VARARGS),
856857

857858
/* Checksum primitives */

test/test_crypto.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# SPDX-License-Identifier: Apache-2.0.
33

44

5-
from awscrt.io import LogLevel, init_logging
65
from test import NativeResourceTest
76
from awscrt.crypto import Hash, RSA, RSAEncryptionAlgorithm, RSASignatureAlgorithm, ED25519, ED25519ExportFormat, EC, ECType, ECRawSignature
87
import base64
@@ -388,6 +387,25 @@ def test_ec_asn1_signing_roundtrip(self):
388387

389388
self.assertTrue(ec.verify(digest, signature))
390389

390+
def test_ec_signature_decode_pair(self):
391+
392+
signature = bytes([0x30, 0x42, 0x02, 0x1f, 0x2d, 0x2a, 0xad, 0xce, 0xbc, 0x1b, 0x3f, 0x78,
393+
0xec, 0xd1, 0x12, 0x53, 0x9e, 0xc0, 0xe3, 0x44, 0x7b, 0x37, 0x5f, 0x6a,
394+
0x99, 0xca, 0x0b, 0x27, 0xb5, 0x4c, 0x31, 0xda, 0x0e, 0x6c, 0x5e, 0x02,
395+
0x1f, 0x47, 0xfb, 0x3d, 0xbd, 0xff, 0xb8, 0x58, 0xf4, 0xba, 0x8a, 0x03,
396+
0xe7, 0xb4, 0x83, 0xe6, 0xb8, 0xc9, 0x46, 0xa8, 0x0a, 0xd8, 0x46, 0xfa,
397+
0x80, 0x0a, 0xd8, 0xca, 0xc5, 0x3f, 0x8e, 0xbd])
398+
399+
padded_sig = EC.decode_der_signature_to_padded_pair(signature=signature, pad_to=32)
400+
401+
expected_sig = bytes([0x00, 0x2d, 0x2a, 0xad, 0xce, 0xbc, 0x1b, 0x3f, 0x78, 0xec, 0xd1, 0x12, 0x53,
402+
0x9e, 0xc0, 0xe3, 0x44, 0x7b, 0x37, 0x5f, 0x6a, 0x99, 0xca, 0x0b, 0x27, 0xb5,
403+
0x4c, 0x31, 0xda, 0x0e, 0x6c, 0x5e, 0x00, 0x47, 0xfb, 0x3d, 0xbd, 0xff, 0xb8,
404+
0x58, 0xf4, 0xba, 0x8a, 0x03, 0xe7, 0xb4, 0x83, 0xe6, 0xb8, 0xc9, 0x46, 0xa8,
405+
0x0a, 0xd8, 0x46, 0xfa, 0x80, 0x0a, 0xd8, 0xca, 0xc5, 0x3f, 0x8e, 0xbd])
406+
407+
self.assertEqual(padded_sig, expected_sig)
408+
391409

392410
if __name__ == '__main__':
393411
unittest.main()

0 commit comments

Comments
 (0)