@@ -296,8 +296,12 @@ pub fn PrivateKey.new(opt CurveOptions) !PrivateKey {
296296// sign performs signing the message with the options. By default options,
297297// it will perform hashing before signing the message.
298298pub fn (pv PrivateKey) sign (message []u8 , opt SignerOpts) ! []u8 {
299- digest := calc_digest (pv.key, message, opt)!
300- return pv.sign_message (digest)!
299+ if pv.evpkey != unsafe { nil } {
300+ digest := calc_digest_with_evpkey (pv.evpkey, message, opt)!
301+ return sign_digest (pv.evpkey, digest)!
302+ }
303+ digest := calc_digest_with_eckey (pv.key, message, opt)!
304+ return pv.sign_digest (digest)!
301305}
302306
303307// sign_with_options signs message with the options. It will be deprecated,
@@ -307,18 +311,18 @@ pub fn (pv PrivateKey) sign_with_options(message []u8, opt SignerOpts) ![]u8 {
307311 return pv.sign (message, opt)
308312}
309313
310- // sign_message sign a message with private key.
311- fn (priv_key PrivateKey) sign_message (message []u8 ) ! []u8 {
312- if message .len == 0 {
313- return error ('Message cannot be null or empty' )
314+ // sign_digest sign a digest with private key.
315+ fn (pv PrivateKey) sign_digest (digest []u8 ) ! []u8 {
316+ if digest .len == 0 {
317+ return error ('Digest cannot be null or empty' )
314318 }
315319 mut sig_len := u32 (0 )
316- sig_size := C.ECDSA_size (priv_key .key)
320+ sig_size := C.ECDSA_size (pv .key)
317321 sig := unsafe { malloc (int (sig_size)) }
318- res := C.ECDSA_sign (0 , message .data, message .len, sig, & sig_len, priv_key .key)
322+ res := C.ECDSA_sign (0 , digest .data, digest .len, sig, & sig_len, pv .key)
319323 if res != 1 {
320324 unsafe { free (sig) }
321- return error ('Failed to sign message ' )
325+ return error ('Failed to sign digest ' )
322326 }
323327 signed_data := unsafe { sig.vbytes (int (sig_len)) }
324328 unsafe { free (sig) }
@@ -469,9 +473,13 @@ pub struct PublicKey {
469473// verify verifies a message with the signature are valid with public key provided .
470474// You should provide it with the same SignerOpts used with the `.sign()` call.
471475// or verify would fail (false).
472- pub fn (pub_key PublicKey) verify (message []u8 , sig []u8 , opt SignerOpts) ! bool {
473- digest := calc_digest (pub_key.key, message, opt)!
474- res := C.ECDSA_verify (0 , digest.data, digest.len, sig.data, sig.len, pub_key.key)
476+ pub fn (pb PublicKey) verify (message []u8 , sig []u8 , opt SignerOpts) ! bool {
477+ if pb.evpkey != unsafe { nil } {
478+ digest := calc_digest_with_evpkey (pb.evpkey, message, opt)!
479+ return verify_signature (pb.evpkey, sig, digest)
480+ }
481+ digest := calc_digest_with_eckey (pb.key, message, opt)!
482+ res := C.ECDSA_verify (0 , digest.data, digest.len, sig.data, sig.len, pb.key)
475483 if res == - 1 {
476484 return error ('Failed to verify signature' )
477485 }
@@ -582,9 +590,9 @@ fn calc_digest_with_recommended_hash(key &C.EC_KEY, msg []u8) ![]u8 {
582590 }
583591}
584592
585- // calc_digest tries to calculates digest (hash) of the message based on options provided.
593+ // calc_digest_with_eckey tries to calculates digest (hash) of the message based on options provided.
586594// If the options was .with_no_hash, its has the same behaviour with .with_recommended_hash.
587- fn calc_digest (key & C.EC_KEY, message []u8 , opt SignerOpts) ! []u8 {
595+ fn calc_digest_with_eckey (key & C.EC_KEY, message []u8 , opt SignerOpts) ! []u8 {
588596 if message.len == 0 {
589597 return error ('null-length messages' )
590598 }
@@ -640,3 +648,152 @@ fn calc_digest(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 {
640648 }
641649 return error ('Not should be here' )
642650}
651+
652+ // calc_digest_with_evpkey get the digest of the messages under the EVP_PKEY and options
653+ fn calc_digest_with_evpkey (key & C.EVP_PKEY, message []u8 , opt SignerOpts) ! []u8 {
654+ if message.len == 0 {
655+ return error ('null-length messages' )
656+ }
657+ bits_size := C.EVP_PKEY_get_bits (key)
658+ if bits_size < = 0 {
659+ return error (' bits_size was invalid' )
660+ }
661+ key_size := (bits_size + 7 ) / 8
662+
663+ match opt.hash_config {
664+ .with_no_hash, .with_recommended_hash {
665+ md := default_digest (key)!
666+ return calc_digest_with_md (message, md)!
667+ }
668+ .with_custom_hash {
669+ mut cfg := opt
670+ if ! cfg.allow_custom_hash {
671+ return error ('custom hash was not allowed, set it into true' )
672+ }
673+ if cfg.custom_hash == unsafe { nil } {
674+ return error ('Custom hasher was not defined' )
675+ }
676+ if key_size > cfg.custom_hash.size () {
677+ if ! cfg.allow_smaller_size {
678+ return error ('Hash into smaller size than current key size was not allowed' )
679+ }
680+ }
681+ // we need to reset the custom hash before writes message
682+ cfg.custom_hash.reset ()
683+ _ := cfg.custom_hash.write (message)!
684+ digest := cfg.custom_hash.sum ([]u8 {})
685+
686+ return digest
687+ }
688+ }
689+ return error ('Not should be here' )
690+ }
691+
692+ // sign_digest signs the digest with the key. Under the hood, EVP_PKEY_sign() does not
693+ // hash the data to be signed, and therefore is normally used to sign digests.
694+ fn sign_digest (key & C.EVP_PKEY, digest []u8 ) ! []u8 {
695+ ctx := C.EVP_PKEY_CTX_new (key, 0 )
696+ if ctx == 0 {
697+ C.EVP_PKEY_CTX_free (ctx)
698+ return error ('EVP_PKEY_CTX_new failed' )
699+ }
700+ sin := C.EVP_PKEY_sign_init (ctx)
701+ if sin != 1 {
702+ C.EVP_PKEY_CTX_free (ctx)
703+ return error ('EVP_PKEY_sign_init failed' )
704+ }
705+ // siglen was used to store the size of the signature output. When EVP_PKEY_sign
706+ // was called with NULL signature buffer, siglen will tell maximum size of signature.
707+ siglen := usize (C.EVP_PKEY_size (key))
708+ st := C.EVP_PKEY_sign (ctx, 0 , & siglen, digest.data, digest.len)
709+ if st < = 0 {
710+ C.EVP_PKEY_CTX_free (ctx)
711+ return error ('Get null buffer length on EVP_PKEY_sign' )
712+ }
713+ sig := []u8 {len: int (siglen)}
714+ do := C.EVP_PKEY_sign (ctx, sig.data, & siglen, digest.data, digest.len)
715+ if do < = 0 {
716+ C.EVP_PKEY_CTX_free (ctx)
717+ return error ('EVP_PKEY_sign fails to sign message' )
718+ }
719+ // siglen now contains actual length of the signature buffer.
720+ signed := sig[..siglen].clone ()
721+
722+ // Cleans up
723+ unsafe { sig.free () }
724+ C.EVP_PKEY_CTX_free (ctx)
725+
726+ return signed
727+ }
728+
729+ // verify_signature verifies the signature for the digest under the provided key.
730+ fn verify_signature (key & C.EVP_PKEY, sig []u8 , digest []u8 ) bool {
731+ ctx := C.EVP_PKEY_CTX_new (key, 0 )
732+ if ctx == 0 {
733+ C.EVP_PKEY_CTX_free (ctx)
734+ return false
735+ }
736+ vinit := C.EVP_PKEY_verify_init (ctx)
737+ if vinit != 1 {
738+ C.EVP_PKEY_CTX_free (ctx)
739+ return false
740+ }
741+ res := C.EVP_PKEY_verify (ctx, sig.data, sig.len, digest.data, digest.len)
742+ if res < = 0 {
743+ C.EVP_PKEY_CTX_free (ctx)
744+ return false
745+ }
746+ C.EVP_PKEY_CTX_free (ctx)
747+ return res == 1
748+ }
749+
750+ // calc_digest_with_md get the digest of the msg using md digest algorithm
751+ fn calc_digest_with_md (msg []u8 , md & C.EVP_MD) ! []u8 {
752+ ctx := C.EVP_MD_CTX_new ()
753+ if ctx == 0 {
754+ C.EVP_MD_CTX_free (ctx)
755+ return error ('EVP_MD_CTX_new failed' )
756+ }
757+ nt := C.EVP_DigestInit (ctx, md)
758+ assert nt == 1
759+ upd := C.EVP_DigestUpdate (ctx, msg.data, msg.len)
760+ assert upd == 1
761+
762+ size := usize (C.EVP_MD_get_size (md))
763+ out := []u8 {len: int (size)}
764+
765+ fin := C.EVP_DigestFinal (ctx, out.data, & size)
766+ assert fin == 1
767+
768+ digest := out[..size].clone ()
769+ // cleans up
770+ unsafe { out.free () }
771+ C.EVP_MD_CTX_free (ctx)
772+
773+ return digest
774+ }
775+
776+ // default_digest gets the default digest (hash) algorithm for this key.
777+ fn default_digest (key & C.EVP_PKEY) ! & C.EVP_MD {
778+ // get bits size of this key
779+ bits_size := C.EVP_PKEY_get_bits (key)
780+ if bits_size < = 0 {
781+ return error (' this size isnt available.' )
782+ }
783+ // based on this bits_size, choose appropriate digest algorithm
784+ match true {
785+ bits_size < = 256 {
786+ return voidptr (C.EVP_sha256 ())
787+ }
788+ bits_size > 256 && bits_size < = 384 {
789+ return voidptr (C.EVP_sha384 ())
790+ }
791+ bits_size > 384 {
792+ return voidptr (C.EVP_sha512 ())
793+ }
794+ else {
795+ return error ('Unsupported bits size' )
796+ }
797+ }
798+ return error ('should not here' )
799+ }
0 commit comments