microsoft / go-crypto-winnative

Go crypto backend for Windows using CNG
MIT License
28 stars 3 forks source link

Lazy initialize hashes #49

Closed qmuntal closed 1 year ago

qmuntal commented 1 year ago

It is a common pattern in this package to accept a h func() hash.Hash argument, call ch := h(), use ch to know the hash type being used and then discard ch without performing any hash. Cumbersome, but it's what it is to be compatible with the upstream boring API. For example from the HKDF implementation:

func newHKDF(h func() hash.Hash, secret, salt []byte, info []byte) (*hkdf, error) {
    ch := h()
    hashID := hashToID(ch)
        ...
    err := setString(bcrypt.HANDLE(kh), bcrypt.HKDF_HASH_ALGORITHM, hashID)
        ... 
    k := &hkdf{kh, info, ch.Size(), nil}
        ... 
}

This process is currently allocating one slice and and calling a cgo function that will not be used at all, which hurts the performance across the board.

This PR refactors how hashes are implemented so that newHashX only initialize what is really necessary to identify the hash algorithm, defering other steps to when it is really needed.

While here, fix PBKDF2 benchmarks.

goos: windows
goarch: amd64
pkg: github.com/microsoft/go-crypto-winnative/cng
cpu: Intel(R) Core(TM) i7-10850H CPU @ 2.70GHz
                      │   old.txt    │               new.txt               │
                      │    sec/op    │    sec/op     vs base               │
Encrypt-12              336.6n ±  3%   326.5n ±  4%   -2.99% (p=0.028 n=6)
Decrypt-12              338.4n ±  0%   329.8n ± 10%        ~ (p=0.065 n=6)
TripleDESEncrypt-12     337.1n ±  1%   329.8n ±  1%   -2.17% (p=0.002 n=6)
TripleDESDecrypt-12     336.2n ±  2%   329.8n ±  1%   -1.89% (p=0.002 n=6)
SignECDSA-12            178.1µ ±  1%   174.3µ ±  1%   -2.12% (p=0.002 n=6)
VerifyECDSA-12          193.7µ ±  1%   191.6µ ±  1%        ~ (p=0.065 n=6)
GenerateKeyECDSA-12     175.4µ ±  1%   176.9µ ± 24%        ~ (p=0.818 n=6)
SHA256_8Bytes-12        1.211µ ±  2%   1.228µ ±  1%   +1.36% (p=0.017 n=6)
SHA256_OneShot-12       590.3n ±  1%   594.4n ±  1%        ~ (p=0.288 n=6)
HKDF32ByteSHA256Single-12   8.052µ ±  5%   6.778µ ±  8%  -15.82% (p=0.002 n=6)
HKDF8ByteSHA256Stream-12    70.88µ ±  4%   71.35µ ±  6%        ~ (p=0.310 n=6)
32ByteSHA256Stream-12   71.62µ ±  2%   71.29µ ±  7%        ~ (p=0.965 n=6)
PBKDF2HMACSHA1-12       1.021m ±  1%   1.014m ±  1%        ~ (p=0.394 n=6)
PBKDF2HMACSHA256-12     2.256m ±  1%   2.258m ±  1%        ~ (p=0.589 n=6)
EncryptRSAPKCS1-12      16.80µ ±  1%   16.77µ ±  2%        ~ (p=0.781 n=6)
GenerateKeyRSA-12       51.22m ± 29%   49.99m ± 11%        ~ (p=0.818 n=6)
geomean                 22.57µ         22.14µ         -1.87%

                      │    old.txt     │                new.txt                │
                      │      B/op      │     B/op      vs base                 │
Encrypt-12                0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=6) ¹
Decrypt-12                0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=6) ¹
TripleDESEncrypt-12       0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=6) ¹
TripleDESDecrypt-12       0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=6) ¹
SignECDSA-12              64.00 ± 0%       64.00 ± 0%        ~ (p=1.000 n=6) ¹
VerifyECDSA-12            64.00 ± 0%       64.00 ± 0%        ~ (p=1.000 n=6) ¹
GenerateKeyECDSA-12       112.0 ± 0%       112.0 ± 0%        ~ (p=1.000 n=6) ¹
SHA256_8Bytes-12          0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=6) ¹
SHA256_OneShot-12         0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=6) ¹
HKDF32ByteSHA256Single-12     448.0 ± 0%       352.0 ± 0%  -21.43% (p=0.002 n=6)
HKDF8ByteSHA256Stream-12      33.00 ± 3%       33.00 ± 3%        ~ (p=1.000 n=6)
32ByteSHA256Stream-12     135.5 ± 0%       135.0 ± 0%        ~ (p=0.182 n=6)
PBKDF2HMACSHA1-12        128.00 ± 0%       88.00 ± 0%  -31.25% (p=0.002 n=6)
PBKDF2HMACSHA256-12      144.00 ± 0%       96.00 ± 0%  -33.33% (p=0.002 n=6)
EncryptRSAPKCS1-12        416.0 ± 0%       416.0 ± 0%        ~ (p=1.000 n=6) ¹
GenerateKeyRSA-12       1.250Ki ± 0%     1.250Ki ± 0%        ~ (p=1.000 n=6) ¹
geomean                              ²                  -6.21%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                      │   old.txt    │               new.txt               │
                      │  allocs/op   │ allocs/op   vs base                 │
Encrypt-12              0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
Decrypt-12              0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
TripleDESEncrypt-12     0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
TripleDESDecrypt-12     0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
SignECDSA-12            1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=6) ¹
VerifyECDSA-12          1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=6) ¹
GenerateKeyECDSA-12     1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=6) ¹
SHA256_8Bytes-12        0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
SHA256_OneShot-12       0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
32ByteSHA256Single-12   8.000 ± 0%     6.000 ± 0%  -25.00% (p=0.002 n=6)
8ByteSHA256Stream-12    0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
32ByteSHA256Stream-12   0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=6) ¹
PBKDF2HMACSHA1-12       3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.002 n=6)
PBKDF2HMACSHA256-12     3.000 ± 0%     2.000 ± 0%  -33.33% (p=0.002 n=6)
EncryptRSAPKCS1-12      1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=6) ¹
GenerateKeyRSA-12       1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=6) ¹
geomean                            ²                -6.64%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean