def ec_pubkey_combine(ctx, pubkeys): '''Add a number of public keys together. Args: ctx (secp256k1_context*): pointer to a context object pubkeys (list): list of pubkeys to add together Returns: (int, secp256k1_pubkey*): (1: the sum of the public keys is valid 0: the sum of the public keys is not valid, pointer to a public key object for placing the resulting public key (cannot be NULL)) ''' # Validate context utils.validate_context(ctx) # Number of public keys to add together n = len(pubkeys) # Pointer to array of pointers to public keys (cannot be null) ins = ffi.new('secp256k1_pubkey[]', n) ins = ffi.new('secp256k1_pubkey*[]', [pk.as_cffi_pointer() for pk in pubkeys]) # Pointer to a public key object for placing the resulting public key out = ffi.new('secp256k1_pubkey *') return (lib.secp256k1_ec_pubkey_combine(ctx, out, ins, n), out)
def ec_pubkey_parse(ctx, ser_pub): '''Parse a variable-length public key into the pubkey object. This function supports parsing compressed (33 bytes, header byte 0x02 or 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header byte 0x06 or 0x07) format public keys. Args: ctx (secp256k1_context*): secp256k1 context object ser_pub (bytes): pointer to a serialized public key Returns: (int, secp256k1_pubkey*): (1 if the public key was fully valid. 0 if the public key could not be parsed or is invalid, pointer to a secp256k1_pubkey containing an initialized public key) ''' # Validate context utils.validate_context(ctx) # Validate input utils.validate_public_key_ser(ser_pub) # Length of the array pointed to by input inputlen = len(ser_pub) # Pointer to a pubkey object. If 1 is returned, it is set to a parsed # version of input. If not, its value is undefined. pubkey = ffi.new('secp256k1_pubkey *') return (lib.secp256k1_ec_pubkey_parse(ctx, pubkey, ser_pub, inputlen), pubkey)
def ec_pubkey_create(ctx, seckey): '''Compute the public key for a secret key. Args: ctx (secp256k1_context*): a secp256k1 context object, initialized for signing (cannot be NULL) seckey (bytes): pointer to a 32-byte private key (cannot be NULL) Returns: (int, secp256k1_pubkey): (1 if secret was valid, public key stores, 0 if secret was invalid, try again, pointer to the created public key (cannot be NULL)) ''' # Validate context utils.validate_context(ctx) # Validate secret key utils.validate_secret_key_ser(seckey) # Pointer to the created public key pubkey = ffi.new('secp256k1_pubkey *') # Compute the public key for a secret key return (lib.secp256k1_ec_pubkey_create(ctx, pubkey, seckey), pubkey)
def ecdsa_signature_parse_der(ctx, ser_sig): '''Parse a DER ECDSA signature. This function will accept any valid DER encoded signature, even if the encoded numbers are out of range. After the call, sig will always be initialized. If parsing failed or the encoded numbers are out of range, signature validation with it is guaranteed to fail for every message and public key. Args: ctx (secp256k1_context*): a secp256k1 context object ser_sig (bytes): a pointer to the signature to be parsed Returns: (int, secp256k1_ecdsa_signature*): (1 when the signature could be parsed, 0 otherwise, a pointer to a signature object) ''' # Validate context utils.validate_context(ctx) # Validate signature utils.validate_signature_ser(ser_sig) # Length of the array pointed to be input inputlen = len(ser_sig) # Pointer to a signature object sig = ffi.new('secp256k1_ecdsa_signature *') # Parse a DER ECDSA signature return (lib.secp256k1_ecdsa_signature_parse_der(ctx, sig, ser_sig, inputlen), sig)
def ecdsa_signature_parse_compact(ctx, input64): '''Parse an ECDSA signature in compact (64 bytes) format. The signature must consist of a 32-byte big endian R value, followed by a 32-byte big endian S value. If R or S fall outside of [0..order-1], the encoding is invalid. R and S with value 0 are allowed in the encoding. After the call, sig will always be initialized. If parsing failed or R or S are zero, the resulting sig value is guaranteed to fail validation for any message and public key. Args: ctx (secp256k1_context*): a secp256k1 context object input64 (bytes): a pointer to the 64-byte array to parse Returns: (int, secp256k1_ecdsa_signature*): (1 when the signature could be parsed, 0 otherwise, a pointer to a signature object) ''' # Validate context utils.validate_context(ctx) # Pointer to a signature object sig = ffi.new('secp256k1_ecdsa_signature *') # Parse an ECDSA signature in compact (64 bytes) format return (lib.secp256k1_ecdsa_signature_parse_compact(ctx, sig, input64), sig)
def ec_pubkey_serialize(ctx, pubkey, flags): '''Serialize a pubkey object into a serialized byte sequence. Args: ctx (secp256k1_context*): a secp256k1 context object pubkey (secp256k1_pubkey*): a pointer to a secp256k1_pubkey containing an initialized public key flags (int): SECP256K1_EC_COMPRESSED if serialization should be in compressed format, otherwise SECP256K1_EC_UNCOMPRESSED Returns: (int, ctype 'char[33]', size_t*): (1, a pointer to a 65-byte (if compressed==0) or 33-byte (if compressed==1) byte array to place the serialized key in, Pointer to an integer which is initially set to the size of the output, and is overwritten with the written size) ''' # Validate context utils.validate_context(ctx) # Validate public key utils.validate_public_key(pubkey) # Validate flags if flags == lib.SECP256K1_EC_COMPRESSED: publen = 33 elif flags == lib.SECP256K1_EC_UNCOMPRESSED: publen = 65 else: raise ValueError('Invalid serialized compression format flag.') # Pointer to a 33- or 65-byte array to place the serialized key in output = ffi.new('char[]', publen) # Pointer to an integer which is initially set to the size of the output, # and is overwritten with the written size outputlen = ffi.new('size_t *', publen) output_length = int(ffi.cast('uint32_t', outputlen[0])) return (lib.secp256k1_ec_pubkey_serialize(ctx, output, outputlen, pubkey, flags), output[0:output_length], outputlen)
def ecdsa_signature_serialize_der(ctx, sig, outputlen=74): '''Serialize an ECDSA signature in DER format. Args: ctx (secp256k1_context*): a secp256k1 context object sig (secp256k1_ecdsa_signature*): a pointer to an initialized signature object outputlen (int): pointer to a length of output Returns: (int, unsigned char[], size_t*): (1 if enough space was available to serialize, 0 otherwise, a pointer to an array to store the DER serialization, a pointer to a length of the serialization (even if 0 was returned)) ''' # Validate context utils.validate_context(ctx) # Validate signature utils.validate_signature(sig) # Pointer to an array to store the DER serialization output = ffi.new('unsigned char[]', outputlen) # Pointer to a length integer outputlen = ffi.new('size_t *', outputlen) res = lib.secp256k1_ecdsa_signature_serialize_der(ctx, output, outputlen, sig) output_length = int(ffi.cast('uint32_t', outputlen[0])) # Serialize an ECDSA signature in DER format return (res, output[0:output_length], outputlen)
def ecdsa_sign(ctx, msg32, seckey, noncefp=ffi.NULL, ndata=ffi.NULL): '''Create an ECDSA signature. The created signature is always in lower-S form. See secp256k1_ecdsa_signature_normalize for more details. Args: ctx (secp256k1_context*): a secp256k1 context object, initialized for signing msg32 (bytes): the 32-byte message hash being signed (cannot be NULL) seckey (bytes): pointer to a 32-byte secret key (cannot be NULL) noncefp (secp256k1_nonce_function): pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used ndata (void*): pointer to arbitrary data used by the nonce generation function (can be NULL) Returns: (int, secp256k1_ecdsa_signature*): (1: signature created, 0: the nonce generation function failed, or the private key was invalid, pointer to an array where the signature will be placed (cannot be NULL)) ''' # Validate context utils.validate_context(ctx) # Validate msg32 utils.validate_msg32_ser(msg32) # Validate secret key utils.validate_secret_key_ser(seckey) # Validate noncefp utils.validate_noncefp(noncefp) # Validate ndata utils.validate_ndata(ndata) sig = ffi.new('secp256k1_ecdsa_signature *') return (lib.secp256k1_ecdsa_sign(ctx, sig, msg32, seckey, noncefp, ndata), sig)
def ecdsa_signature_serialize_compact(ctx, sig): '''Serialize an ECDSA signature in compact (64 byte) format. See secp256k1_ecdsa_signature_parse_compact for details about the encoding. Args: ctx (secp256k1_context*): a secp256k1 context object sig (secp256k1_ecdsa_signature*): a pointer to an initialized signature object Returns: (int, unsigned char): (1, a pointer to a 64-byte array to store the compact serialization) ''' # Validate context utils.validate_context(ctx) # Validate signature utils.validate_signature(sig) # Pointer to a 64-byte array to store the compact serialization output64 = ffi.new('unsigned char[]', 64) return (lib.secp256k1_ecdsa_signature_serialize_compact( ctx, output64, sig), output64)
def ecdsa_signature_normalize(ctx, sigout, sigin): '''Convert a signature to a normalized lower-S form. With ECDSA a third-party can forge a second distinct signature of the same message, given a single initial signature, but without knowing the key. This is done by negating the S value modulo the order of the curve, 'flipping' the sign of the random point R which is not included in the signature. Forgery of the same message isn't universally problematic, but in systems where message malleability or uniqueness of signatures is important this can cause issues. This forgery can be blocked by all verifiers forcing signers to use a normalized form. The lower-S form reduces the size of signatures slightly on average when variable length encodings (such as DER) are used and is cheap to verify, making it a good choice. Security of always using lower-S is assured because anyone can trivially modify a signature after the fact to enforce this property anyway. The lower S value is always between 0x1 and 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, inclusive. No other forms of ECDSA malleability are known and none seem likely, but there is no formal proof that ECDSA, even with this additional restriction, is free of other malleability. Commonly used serialization schemes will also accept various non-unique encodings, so care should be taken when this property is required for an application. The secp256k1_ecdsa_sign function will by default create signatures in the lower-S form, and secp256k1_ecdsa_verify will not accept others. In case signatures come from a system that cannot enforce this property, secp256k1_ecdsa_signature_normalize must be called before verification. Args: ctx (secp256k1_context*): a secp256k1 context object sigin (secp256k1_ecdsa_signature*): a pointer to a signature to check/normalize (cannot be NULL, can be identical to sigout) Returns: (int, secp256k1_ecdsa_signature*): (1 if sigin was not normalized, 0 if it already was, a pointer to a signature to fill with the normalized form, or copy if the input was already normalized. (can be NULL if you're only interested in whether the input was already normalized). ''' # Validate context utils.validate_context(ctx) # Validate sig utils.validate_signature(sigin) # Pointer to a signature to fill witht he normalized form, or copy if the # input was already normalized sigout = ffi.new('secp256k1_ecdsa_signature *') return (lib.secp256k1_ecdsa_signature_normalize(ctx, sigout, sigin), sigout)