From 71c89f0cb987b6ace4b3153b7a8f8b1fa0a9236c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:47:56 +0000 Subject: [PATCH] Bump github.com/grafana/xk6-webcrypto from 0.4.0 to 0.5.0 Bumps [github.com/grafana/xk6-webcrypto](https://github.com/grafana/xk6-webcrypto) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/grafana/xk6-webcrypto/releases) - [Commits](https://github.com/grafana/xk6-webcrypto/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: github.com/grafana/xk6-webcrypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../xk6-webcrypto/webcrypto/algorithm.go | 39 +- .../xk6-webcrypto/webcrypto/elliptic_curve.go | 5 +- .../xk6-webcrypto/webcrypto/encryption.go | 15 +- .../grafana/xk6-webcrypto/webcrypto/errors.go | 8 + .../grafana/xk6-webcrypto/webcrypto/hash.go | 57 +++ .../grafana/xk6-webcrypto/webcrypto/jwk.go | 136 ++++++ .../grafana/xk6-webcrypto/webcrypto/key.go | 12 +- .../grafana/xk6-webcrypto/webcrypto/params.go | 19 +- .../grafana/xk6-webcrypto/webcrypto/rsa.go | 459 ++++++++++++++++++ .../grafana/xk6-webcrypto/webcrypto/signer.go | 4 + .../webcrypto/{goja.go => sobek.go} | 0 .../xk6-webcrypto/webcrypto/subtle_crypto.go | 150 +++--- .../xk6-webcrypto/webcrypto/test_setup.go | 102 ---- vendor/modules.txt | 4 +- 16 files changed, 797 insertions(+), 219 deletions(-) create mode 100644 vendor/github.com/grafana/xk6-webcrypto/webcrypto/rsa.go rename vendor/github.com/grafana/xk6-webcrypto/webcrypto/{goja.go => sobek.go} (100%) delete mode 100644 vendor/github.com/grafana/xk6-webcrypto/webcrypto/test_setup.go diff --git a/go.mod b/go.mod index a020963430a..4bf5468aa1b 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/grafana/xk6-output-opentelemetry v0.3.0 github.com/grafana/xk6-output-prometheus-remote v0.4.0 github.com/grafana/xk6-redis v0.3.1 - github.com/grafana/xk6-webcrypto v0.4.0 + github.com/grafana/xk6-webcrypto v0.5.0 github.com/grafana/xk6-websockets v0.7.2 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc diff --git a/go.sum b/go.sum index d3a12152fbb..c58e6339ca4 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/grafana/xk6-output-prometheus-remote v0.4.0 h1:7k3xjuKaD9BwcX8iuu5v6P github.com/grafana/xk6-output-prometheus-remote v0.4.0/go.mod h1:esXXthLoVp9JUdGkECRthESVYu0TQTR24wrx2nRM9ak= github.com/grafana/xk6-redis v0.3.1 h1:RqfmMLNx7vekBxwuTrFP9ErxeY/0H07a3HpQJYXYDjc= github.com/grafana/xk6-redis v0.3.1/go.mod h1:3e/U9i1Nm3WEaMy4nZSGMjVf8ZsFau+aXurYJhJ7MfQ= -github.com/grafana/xk6-webcrypto v0.4.0 h1:CXRGkvVg8snYEyGCq3d5XGzDPxTPJ1m5CS68jPdtZZk= -github.com/grafana/xk6-webcrypto v0.4.0/go.mod h1:+THllImZ8OWlsFc8llWqvzzjottlGdXq/7rIQ16zmFs= +github.com/grafana/xk6-webcrypto v0.5.0 h1:a5NMG/4itLDWprn5XbGaARwUdGPy9wO9z35Z7bDjG1k= +github.com/grafana/xk6-webcrypto v0.5.0/go.mod h1:yZMp9ZjcxLZML2ljcK6CxTI+XTP59vivtKszaH5xIE4= github.com/grafana/xk6-websockets v0.7.2 h1:hwZfk+1zMLJZ2vXqy8WqShG4toHgY6Gw7EdFU37dSXg= github.com/grafana/xk6-websockets v0.7.2/go.mod h1:91oE+otLmjYsPwBvxfv1+6tmoXKPZRPOXTnJveAs5Nk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/algorithm.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/algorithm.go index b7bb448028f..9b9df5b4767 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/algorithm.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/algorithm.go @@ -1,6 +1,7 @@ package webcrypto import ( + "fmt" "reflect" "strings" @@ -145,16 +146,30 @@ func normalizeAlgorithm(rt *sobek.Runtime, v sobek.Value, op AlgorithmIdentifier return Algorithm{}, NewError(SyntaxError, "algorithm cannot be interpreted as a string or an object") } + algorithm.Name = normalizeAlgorithmName(algorithm.Name) + + if !isRegisteredAlgorithm(algorithm.Name, op) { + return Algorithm{}, NewError( + NotSupportedError, + fmt.Sprintf("algorithm %q doesn't support (in implementation) operation %q", algorithm.Name, op), + ) + } + + return algorithm, nil +} + +func normalizeAlgorithmName(name string) string { // Algorithm identifiers are always upper cased. // A registered algorithm provided in lower case format, should // be considered valid. - algorithm.Name = strings.ToUpper(algorithm.Name) + name = strings.ToUpper(name) - if !isRegisteredAlgorithm(algorithm.Name, op) { - return Algorithm{}, NewError(NotSupportedError, "unsupported algorithm: "+algorithm.Name) + // exception is made for RSASSA-PKCS1-v1_5 + if name == strings.ToUpper(RSASsaPkcs1v15) { + return RSASsaPkcs1v15 } - return algorithm, nil + return name } // isRegisteredAlgorithm returns true if the given algorithm name is registered @@ -171,13 +186,17 @@ func isRegisteredAlgorithm(algorithmName string, forOperation string) bool { return isAesAlgorithm(algorithmName) || isHashAlgorithm(algorithmName) || algorithmName == HMAC || - isEllipticCurve(algorithmName) + isEllipticCurve(algorithmName) || + isRSAAlgorithm(algorithmName) case OperationIdentifierExportKey, OperationIdentifierImportKey: - return isAesAlgorithm(algorithmName) || algorithmName == HMAC || isEllipticCurve(algorithmName) + return isAesAlgorithm(algorithmName) || + algorithmName == HMAC || + isEllipticCurve(algorithmName) || + isRSAAlgorithm(algorithmName) case OperationIdentifierEncrypt, OperationIdentifierDecrypt: - return isAesAlgorithm(algorithmName) + return isAesAlgorithm(algorithmName) || algorithmName == RSAOaep case OperationIdentifierSign, OperationIdentifierVerify: - return algorithmName == HMAC || algorithmName == ECDSA + return algorithmName == HMAC || algorithmName == ECDSA || algorithmName == RSAPss || algorithmName == RSASsaPkcs1v15 default: return false } @@ -191,6 +210,10 @@ func isHashAlgorithm(algorithmName string) bool { return algorithmName == SHA1 || algorithmName == SHA256 || algorithmName == SHA384 || algorithmName == SHA512 } +func isRSAAlgorithm(algorithmName string) bool { + return algorithmName == RSASsaPkcs1v15 || algorithmName == RSAPss || algorithmName == RSAOaep +} + // hasAlg an internal interface that helps us to identify // if a given object has an algorithm method. type hasAlg interface { diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/elliptic_curve.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/elliptic_curve.go index 1cb48637103..a03e6ef162a 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/elliptic_curve.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/elliptic_curve.go @@ -316,7 +316,10 @@ func (ecgp *ECKeyGenParams) GenerateKey( Type: PublicCryptoKeyType, Extractable: true, Algorithm: alg, - Usages: publicKeyUsages, + Usages: UsageIntersection( + publicKeyUsages, + keyUsages, + ), } var err error diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/encryption.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/encryption.go index 2e6d3fcb995..9a9be38fb78 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/encryption.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/encryption.go @@ -2,6 +2,7 @@ package webcrypto import ( "fmt" + "reflect" "github.com/grafana/sobek" ) @@ -26,32 +27,32 @@ type EncryptDecrypter interface { // algorithm and parameters `sobek.Value`. // // The returned instance can be used to encrypt/decrypt data using the -// corresponding AES algorithm. +// corresponding algorithm. func newEncryptDecrypter( rt *sobek.Runtime, algorithm Algorithm, params sobek.Value, ) (EncryptDecrypter, error) { var ed EncryptDecrypter - var paramsObjectName string var err error switch algorithm.Name { case AESCbc: ed = new(AESCBCParams) - paramsObjectName = "AesCbcParams" case AESCtr: ed = new(AESCTRParams) - paramsObjectName = "AesCtrParams" case AESGcm: ed = new(AESGCMParams) - paramsObjectName = "AesGcmParams" + case RSAOaep: + ed = new(RSAOaepParams) default: - return nil, NewError(NotSupportedError, "unsupported algorithm") + return nil, NewError(NotSupportedError, "unsupported algorithm "+algorithm.Name) } if err = rt.ExportTo(params, ed); err != nil { - errMsg := fmt.Sprintf("invalid algorithm parameters, unable to interpret as %sParams object", paramsObjectName) + structType := reflect.TypeOf(ed) + + errMsg := fmt.Sprintf("invalid algorithm parameters, unable to interpret as %q object", structType.Name()) return nil, NewError(SyntaxError, errMsg) } diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/errors.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/errors.go index 4ceb9c78d64..60f926079e9 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/errors.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/errors.go @@ -35,6 +35,14 @@ const ( // QuotaExceededError is the error thrown if the byteLength of a typedArray // exceeds 65,536. QuotaExceededError = "QuotaExceededError" + + // NotImplemented means that we have not implemented the feature yet. + NotImplemented = "NotImplemented" +) + +const ( + errMsgNotExpectedPublicKey = "given CryptoKey is not a public %s key, it's %T" + errMsgNotExpectedPrivateKey = "given CryptoKey is not a private %s key, it's %T" ) // Error represents a custom error emitted by the diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/hash.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/hash.go index 87e7634cf43..0e921690505 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/hash.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/hash.go @@ -3,6 +3,10 @@ package webcrypto import ( "crypto" "hash" + "reflect" + + "github.com/grafana/sobek" + "go.k6.io/k6/js/common" ) // getHashFn returns the hash function associated with the given name. @@ -29,3 +33,56 @@ func getHashFn(name string) (func() hash.Hash, bool) { type hasHash interface { hash() string } + +func mapHashFn(hash AlgorithmIdentifier) (crypto.Hash, error) { + unknownHash := crypto.Hash(0) + + switch hash { + case SHA1: + return crypto.SHA1, nil + case SHA256: + return crypto.SHA256, nil + case SHA384: + return crypto.SHA384, nil + case SHA512: + return crypto.SHA512, nil + default: + return unknownHash, NewError(NotSupportedError, "hash algorithm is not supported "+hash) + } +} + +// extractHash tries to extract the hash from the given parameters. +func extractHash(rt *sobek.Runtime, params sobek.Value) (Algorithm, error) { + v, err := traverseObject(rt, params, "hash") + if err != nil { + return Algorithm{}, NewError(SyntaxError, "could not get hash from algorithm parameter") + } + + if common.IsNullish(v) { + return Algorithm{}, NewError(TypeError, "hash is null or undefined") + } + + var hashName string + if v.ExportType().Kind() == reflect.String { + // try string first + if !isHashAlgorithm(v.ToString().String()) { + return Algorithm{}, NewError(NotSupportedError, "hash algorithm is not supported "+v.ToString().String()) + } + + hashName = v.ToString().String() + } else { + // otherwise, it should be an object + name := v.ToObject(rt).Get("name") + if common.IsNullish(name) { + return Algorithm{}, NewError(TypeError, "hash name is null or undefined") + } + + hashName = name.ToString().String() + } + + if !isHashAlgorithm(hashName) { + return Algorithm{}, NewError(NotSupportedError, "hash algorithm is not supported "+hashName) + } + + return Algorithm{Name: hashName}, nil +} diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/jwk.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/jwk.go index 3bb666b6f7f..36f12cbcd5c 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/jwk.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/jwk.go @@ -4,6 +4,7 @@ import ( "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" + "crypto/rsa" "encoding/json" "errors" "fmt" @@ -290,3 +291,138 @@ func importECDHJWK(_ EllipticCurveKind, jsonKeyData []byte) (any, CryptoKeyType, return nil, UnknownCryptoKeyType, errors.New("input isn't a valid ECDH key") } } + +type rsaJWK struct { + Kty string `json:"kty"` // Key Type + N string `json:"n"` // Modulus + E string `json:"e"` // Exponent + D string `json:"d,omitempty"` // Private exponent + P string `json:"p,omitempty"` // First prime factor + Q string `json:"q,omitempty"` // Second prime factor + Dp string `json:"dp,omitempty"` // Exponent1 + Dq string `json:"dq,omitempty"` // Exponent2 + Qi string `json:"qi,omitempty"` // Coefficient +} + +func (jwk *rsaJWK) validate() error { + if jwk.Kty != "RSA" { + return fmt.Errorf("invalid key type: %s", jwk.Kty) + } + + if jwk.N == "" { + return errors.New("modulus (n) is required") + } + + if jwk.E == "" { + return errors.New("exponent (e) is required") + } + + // TODO: consider validating the other fields in future + return nil +} + +func importRSAJWK(jsonKeyData []byte) (any, CryptoKeyType, int, error) { + var jwk rsaJWK + if err := json.Unmarshal(jsonKeyData, &jwk); err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to parse input as RSA JWK key: %w", err) + } + + if err := jwk.validate(); err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("invalid RSA JWK key: %w", err) + } + + // decode the various key components + nBytes, err := base64URLDecode(jwk.N) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode modulus: %w", err) + } + eBytes, err := base64URLDecode(jwk.E) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode exponent: %w", err) + } + + // convert exponent to an integer + eInt := new(big.Int).SetBytes(eBytes).Int64() + pubKey := rsa.PublicKey{ + N: new(big.Int).SetBytes(nBytes), + E: int(eInt), + } + + // if the private exponent is missing, return the public key + if jwk.D == "" { + return pubKey, PublicCryptoKeyType, pubKey.N.BitLen(), nil + } + + dBytes, err := base64URLDecode(jwk.D) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode private exponent: %w", err) + } + pBytes, err := base64URLDecode(jwk.P) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode first prime factor: %w", err) + } + qBytes, err := base64URLDecode(jwk.Q) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode second prime factor: %w", err) + } + dpBytes, err := base64URLDecode(jwk.Dp) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode first exponent: %w", err) + } + dqBytes, err := base64URLDecode(jwk.Dq) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode second exponent: %w", err) + } + qiBytes, err := base64URLDecode(jwk.Qi) + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode coefficient: %w", err) + } + + privKey := &rsa.PrivateKey{ + PublicKey: pubKey, + D: new(big.Int).SetBytes(dBytes), + Primes: []*big.Int{ + new(big.Int).SetBytes(pBytes), + new(big.Int).SetBytes(qBytes), + }, + Precomputed: rsa.PrecomputedValues{ + Dp: new(big.Int).SetBytes(dpBytes), + Dq: new(big.Int).SetBytes(dqBytes), + Qinv: new(big.Int).SetBytes(qiBytes), + }, + } + + err = privKey.Validate() + if err != nil { + return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to validate private key: %w", err) + } + + return privKey, PrivateCryptoKeyType, pubKey.N.BitLen(), nil +} + +func exportRSAJWK(key *CryptoKey) (interface{}, error) { + exported := &JsonWebKey{} + exported.Set("kty", "RSA") + + switch rsaKey := key.handle.(type) { + case *rsa.PrivateKey: + exported.Set("n", base64URLEncode(rsaKey.N.Bytes())) + exported.Set("e", base64URLEncode(big.NewInt(int64(rsaKey.E)).Bytes())) + exported.Set("d", base64URLEncode(rsaKey.D.Bytes())) + exported.Set("p", base64URLEncode(rsaKey.Primes[0].Bytes())) + exported.Set("q", base64URLEncode(rsaKey.Primes[1].Bytes())) + exported.Set("dp", base64URLEncode(rsaKey.Precomputed.Dp.Bytes())) + exported.Set("dq", base64URLEncode(rsaKey.Precomputed.Dq.Bytes())) + exported.Set("qi", base64URLEncode(rsaKey.Precomputed.Qinv.Bytes())) + case *rsa.PublicKey: + exported.Set("n", base64URLEncode(rsaKey.N.Bytes())) + exported.Set("e", base64URLEncode(big.NewInt(int64(rsaKey.E)).Bytes())) + case rsa.PublicKey: + exported.Set("n", base64URLEncode(rsaKey.N.Bytes())) + exported.Set("e", base64URLEncode(big.NewInt(int64(rsaKey.E)).Bytes())) + default: + return nil, fmt.Errorf("key's handle isn't an RSA public/private key, got: %T", key.handle) + } + + return exported, nil +} diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/key.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/key.go index 421f3a3ce87..0f56a4b3d13 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/key.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/key.go @@ -2,6 +2,7 @@ package webcrypto import ( "errors" + "strings" "github.com/grafana/sobek" ) @@ -188,8 +189,15 @@ func newKeyGenerator(rt *sobek.Runtime, normalized Algorithm, params sobek.Value kg, err = newHMACKeyGenParams(rt, normalized, params) case ECDH, ECDSA: kg, err = newECKeyGenParams(rt, normalized, params) + case RSASsaPkcs1v15, RSAPss, RSAOaep: + kg, err = newRsaHashedKeyGenParams(rt, normalized, params) default: - return nil, errors.New("key generation not implemented for algorithm " + normalized.Name) + validAlgorithms := []string{AESCbc, AESCtr, AESGcm, AESKw, HMAC, ECDH, ECDSA, RSASsaPkcs1v15, RSAPss, RSAOaep} + return nil, NewError( + NotImplemented, + "unsupported key generation algorithm '"+normalized.Name+"', "+ + "accepted values are: "+strings.Join(validAlgorithms, ", "), + ) } if err != nil { @@ -216,6 +224,8 @@ func newKeyImporter(rt *sobek.Runtime, normalized Algorithm, params sobek.Value) ki, err = newHMACImportParams(rt, normalized, params) case ECDH, ECDSA: ki, err = newEcKeyImportParams(rt, normalized, params) + case RSASsaPkcs1v15, RSAPss, RSAOaep: + ki, err = newRsaHashedImportParams(rt, normalized, params) default: return nil, errors.New("key import not implemented for algorithm " + normalized.Name) } diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/params.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/params.go index 8ae9bf815e0..12f26c123e3 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/params.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/params.go @@ -89,9 +89,7 @@ type PBKDF2Params struct { // RSAHashedKeyGenParams represents the object that should be passed as the algorithm // parameter into `SubtleCrypto.GenerateKey`, when generating an RSA key pair. type RSAHashedKeyGenParams struct { - // Name should be set to AlgorithmKindRsassPkcs1v15, - // AlgorithmKindRsaPss, or AlgorithmKindRsaOaep. - Name AlgorithmIdentifier + Algorithm // ModulusLength holds (a Number) the length of the RSA modulus, in bits. // This should be at least 2048. Some organizations are now recommending @@ -105,7 +103,7 @@ type RSAHashedKeyGenParams struct { // Hash represents the name of the digest function to use. You can // use any of the following: DigestKindSha256, DigestKindSha384, // or DigestKindSha512. - Hash string + Hash Algorithm } // RSAHashedImportParams represents the object that should be passed as the @@ -113,23 +111,19 @@ type RSAHashedKeyGenParams struct { // importing any RSA-based key pair: that is, when the algorithm is identified as any // of RSASSA-PKCS1-v1_5, RSA-PSS, or RSA-OAEP. type RSAHashedImportParams struct { - // Name should be set to AlgorithmKindRsassPkcs1v15, - // AlgorithmKindRsaPss, or AlgorithmKindRsaOaep depending - // on the algorithm you want to use. - Name string + Algorithm // Hash represents the name of the digest function to use. // Note that although you can technically pass SHA-1 here, this is strongly // discouraged as it is considered vulnerable. - Hash AlgorithmIdentifier + Hash Algorithm } // RSAOaepParams represents the object that should be passed as the algorithm parameter // into `SubtleCrypto.Encrypt`, `SubtleCrypto.Decrypt`, `SubtleCrypto.WrapKey`, or // `SubtleCrypto.UnwrapKey`, when using the RSA_OAEP algorithm. type RSAOaepParams struct { - // Name should be set to "RSA-OAEP" - Name string + Algorithm // Label holds (an ArrayBuffer, a TypedArray, or a DataView) an array of bytes that does not // itself need to be encrypted but which should be bound to the ciphertext. @@ -144,8 +138,7 @@ type RSAOaepParams struct { // parameter into `SubtleCrypto.Sign` or `SubtleCrypto.Verify`, when using the // RSA-PSS algorithm. type RSAPssParams struct { - // Name should be set to AlgorithmKindRsaPss. - Name AlgorithmIdentifier + Algorithm Algorithm // SaltLength holds (a Number) the length of the random salt to use, in bytes. // RFC 3447 says that "typical salt lengths" are either 0 or the length of the output diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/rsa.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/rsa.go new file mode 100644 index 00000000000..bd6ae2cd4b6 --- /dev/null +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/rsa.go @@ -0,0 +1,459 @@ +package webcrypto + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "errors" + "fmt" + "math/big" + + "github.com/grafana/sobek" +) + +// RsaHashedKeyAlgorithm represents the RSA key algorithm as defined by the [specification]. +// +// [specification]: https://www.w3.org/TR/WebCryptoAPI/#RsaHashedKeyAlgorithm-dictionary +type RsaHashedKeyAlgorithm struct { + KeyAlgorithm + + ModulusLength int `js:"modulusLength"` + + Hash Algorithm +} + +var _ KeyGenerator = &RSAHashedKeyGenParams{} + +func newRsaHashedKeyGenParams( + rt *sobek.Runtime, + normalized Algorithm, + params sobek.Value, +) (*RSAHashedKeyGenParams, error) { + modulusLength, err := traverseObject(rt, params, "modulusLength") + if err != nil { + return nil, NewError(SyntaxError, "could not get modulusLength from algorithm parameter") + } + + publicExponentRaw, err := traverseObject(rt, params, "publicExponent") + if err != nil { + return nil, NewError(SyntaxError, "could not get publicExponent from algorithm parameter") + } + + publicExponent, ok := publicExponentRaw.Export().([]byte) + if !ok { + return nil, NewError(OperationError, "publicExponent is not a byte array") + } + + hash, err := extractHash(rt, params) + if err != nil { + return nil, err + } + + return &RSAHashedKeyGenParams{ + Algorithm: normalized, + ModulusLength: int(modulusLength.ToInteger()), + PublicExponent: publicExponent, + Hash: hash, + }, nil +} + +// GenerateKey generates a new RSA key pair. +func (rsakgp *RSAHashedKeyGenParams) GenerateKey( + extractable bool, + keyUsages []CryptoKeyUsage, +) (CryptoKeyGenerationResult, error) { + var privateKeyUsages, publicKeyUsages []CryptoKeyUsage + + publicExponent := int(new(big.Int).SetBytes(rsakgp.PublicExponent).Int64()) + if err := validatePublicExponent(publicExponent); err != nil { + return nil, NewError( + OperationError, + fmt.Sprintf("invalid public exponent: %s", err), + ) + } + + if len(keyUsages) == 0 { + return nil, NewError(SyntaxError, "key usages cannot be empty") + } + + if rsakgp.Algorithm.Name == RSASsaPkcs1v15 || rsakgp.Algorithm.Name == RSAPss { + privateKeyUsages = []CryptoKeyUsage{SignCryptoKeyUsage} + publicKeyUsages = []CryptoKeyUsage{VerifyCryptoKeyUsage} + for _, usage := range keyUsages { + switch usage { + case SignCryptoKeyUsage: + case VerifyCryptoKeyUsage: + continue + default: + return nil, NewError(SyntaxError, "invalid key usage: "+usage) + } + } + } + if rsakgp.Algorithm.Name == RSAOaep { + privateKeyUsages = []CryptoKeyUsage{DecryptCryptoKeyUsage} + publicKeyUsages = []CryptoKeyUsage{EncryptCryptoKeyUsage} + for _, usage := range keyUsages { + switch usage { + case EncryptCryptoKeyUsage: + case DecryptCryptoKeyUsage: + case WrapKeyCryptoKeyUsage: + case UnwrapKeyCryptoKeyUsage: + continue + default: + return nil, NewError(SyntaxError, "invalid key usage: "+usage) + } + } + } + + alg := RsaHashedKeyAlgorithm{ + ModulusLength: rsakgp.ModulusLength, + KeyAlgorithm: KeyAlgorithm{ + Algorithm: rsakgp.Algorithm, + }, + Hash: rsakgp.Hash, + } + + privateKey := &CryptoKey{ + Type: PrivateCryptoKeyType, + Extractable: extractable, + Algorithm: alg, + Usages: UsageIntersection(keyUsages, privateKeyUsages), + } + + publicKey := &CryptoKey{ + Type: PublicCryptoKeyType, + Extractable: true, + Algorithm: alg, + Usages: UsageIntersection(keyUsages, publicKeyUsages), + } + + var err error + privateKey.handle, publicKey.handle, err = generateRSAKeyPair(rsakgp.ModulusLength, publicExponent) + if err != nil { + return nil, err + } + + return &CryptoKeyPair{ + PrivateKey: privateKey, + PublicKey: publicKey, + }, nil +} + +// validatePublicExponent validates the public exponent. +// it's done same way how golang's rsa package does it + additional check for evenness. +func validatePublicExponent(e int) error { + if e%2 == 0 { + return errors.New("public exponent is even") + } + + if e < 2 { + return errors.New("public exponent too small") + } + if e > 1<<31-1 { + return errors.New("public exponent too large") + } + + return nil +} + +func generateRSAKeyPair( + modulusLength int, + publicExponent int, +) (any, any, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, modulusLength) + if err != nil { + return nil, nil, NewError(OperationError, "could not generate RSA key pair") + } + + privateKey.PublicKey.E = publicExponent + + // validate the key pair, since we are setting the public exponent manually + if err := privateKey.Validate(); err != nil { + return nil, nil, NewError(OperationError, "could not validate RSA key pair, check public exponent: "+err.Error()) + } + + return privateKey, privateKey.Public(), nil +} + +func exportRSAKey(ck *CryptoKey, format KeyFormat) (interface{}, error) { + if ck.handle == nil { + return nil, NewError(OperationError, "key data is not accessible") + } + + switch format { + case SpkiKeyFormat: + if ck.Type != PublicCryptoKeyType { + return nil, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPublicKey, "RSA", ck.handle)) + } + + bytes, err := x509.MarshalPKIXPublicKey(ck.handle) + if err != nil { + return nil, NewError(OperationError, "unable to marshal key to SPKI format: "+err.Error()) + } + + return bytes, nil + case Pkcs8KeyFormat: + if ck.Type != PrivateCryptoKeyType { + return nil, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPrivateKey, "RSA", ck.handle)) + } + + bytes, err := x509.MarshalPKCS8PrivateKey(ck.handle) + if err != nil { + return nil, NewError(OperationError, "unable to marshal key to PKCS8 format: "+err.Error()) + } + + return bytes, nil + case JwkKeyFormat: + return exportRSAJWK(ck) + default: + return nil, NewError(NotSupportedError, unsupportedKeyFormatErrorMsg+" "+format) + } +} + +func newRsaHashedImportParams( + rt *sobek.Runtime, + normalized Algorithm, + params sobek.Value, +) (*RSAHashedImportParams, error) { + hash, err := extractHash(rt, params) + if err != nil { + return nil, err + } + + return &RSAHashedImportParams{ + Algorithm: normalized, + Hash: hash, + }, nil +} + +// Ensure that RSAHashedImportParams implements the KeyImporter interface. +var _ KeyImporter = &RSAHashedImportParams{} + +// ImportKey imports a key according to the algorithm described in the specification. +func (rhkip *RSAHashedImportParams) ImportKey( + format KeyFormat, + keyData []byte, + usages []CryptoKeyUsage, +) (*CryptoKey, error) { + var importFn func(keyData []byte) (any, CryptoKeyType, int, error) + + switch { + case format == Pkcs8KeyFormat: + importFn = importRSAPrivateKey + case format == SpkiKeyFormat: + importFn = importRSAPublicKey + case format == JwkKeyFormat: + importFn = importRSAJWK + default: + return nil, NewError( + NotSupportedError, + unsupportedKeyFormatErrorMsg+" "+format+" for algorithm "+rhkip.Algorithm.Name, + ) + } + + handle, keyType, modusLength, err := importFn(keyData) + if err != nil { + return nil, err + } + + return &CryptoKey{ + Algorithm: RsaHashedKeyAlgorithm{ + ModulusLength: modusLength, + KeyAlgorithm: KeyAlgorithm{ + Algorithm: rhkip.Algorithm, + }, + Hash: rhkip.Hash, + }, + Type: keyType, + Usages: usages, + handle: handle, + }, nil +} + +func importRSAPrivateKey(keyData []byte) (any, CryptoKeyType, int, error) { + parsedKey, err := x509.ParsePKCS8PrivateKey(keyData) + if err != nil { + return nil, UnknownCryptoKeyType, 0, NewError(DataError, "unable to import RSA private key data: "+err.Error()) + } + + privateKey, ok := parsedKey.(*rsa.PrivateKey) + if !ok { + return nil, UnknownCryptoKeyType, 0, NewError(DataError, fmt.Sprintf(errMsgNotExpectedPrivateKey, "RSA", privateKey)) + } + + return privateKey, PrivateCryptoKeyType, privateKey.PublicKey.N.BitLen(), nil +} + +func importRSAPublicKey(keyData []byte) (any, CryptoKeyType, int, error) { + parsedKey, err := x509.ParsePKIXPublicKey(keyData) + if err != nil { + return nil, UnknownCryptoKeyType, 0, NewError(DataError, "unable to import RSA public key data: "+err.Error()) + } + + publicKey, ok := parsedKey.(*rsa.PublicKey) + if !ok { + return nil, UnknownCryptoKeyType, 0, NewError(DataError, fmt.Sprintf(errMsgNotExpectedPublicKey, "RSA", publicKey)) + } + + return publicKey, PublicCryptoKeyType, publicKey.N.BitLen(), nil +} + +type rsaSsaPkcs1v15SignerVerifier struct{} + +var _ SignerVerifier = &rsaSsaPkcs1v15SignerVerifier{} + +func (rsasv *rsaSsaPkcs1v15SignerVerifier) Sign(key CryptoKey, data []byte) ([]byte, error) { + hash, err := extractHashFromRSAKey(key) + if err != nil { + return nil, err + } + + hashedData := hash.New() + hashedData.Write(data) + + rsaKey, ok := key.handle.(*rsa.PrivateKey) + if !ok { + return nil, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPrivateKey, "RSA", key.handle)) + } + + signature, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, hash, hashedData.Sum(nil)) + if err != nil { + return nil, NewError(OperationError, "could not sign data: "+err.Error()) + } + + return signature, nil +} + +func (rsasv *rsaSsaPkcs1v15SignerVerifier) Verify(key CryptoKey, signature []byte, data []byte) (bool, error) { + hash, err := extractHashFromRSAKey(key) + if err != nil { + return false, err + } + + hashedData := hash.New() + hashedData.Write(data) + + rsaKey, ok := key.handle.(*rsa.PublicKey) + if !ok { + return false, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPublicKey, "RSA", key.handle)) + } + + err = rsa.VerifyPKCS1v15(rsaKey, hash, hashedData.Sum(nil), signature) + if err != nil { + return false, nil //nolint:nilerr + } + + return true, nil +} + +func extractHashFromRSAKey(key CryptoKey) (crypto.Hash, error) { + unk := crypto.Hash(0) + + rsaHashedAlg, ok := key.Algorithm.(RsaHashedKeyAlgorithm) + if !ok { + return unk, NewError(InvalidAccessError, "key algorithm is not a RSA hashed key algorithm") + } + + return mapHashFn(rsaHashedAlg.Hash.Name) +} + +func newRSAPssParams(rt *sobek.Runtime, normalized Algorithm, params sobek.Value) (*RSAPssParams, error) { + saltLength, err := traverseObject(rt, params, "saltLength") + if err != nil { + return nil, NewError(SyntaxError, "could not get saltLength from algorithm parameter") + } + + return &RSAPssParams{ + Algorithm: normalized, + SaltLength: int(saltLength.ToInteger()), + }, nil +} + +var _ SignerVerifier = &RSAPssParams{} + +// Sign signs the given data. +func (rsasv *RSAPssParams) Sign(key CryptoKey, data []byte) ([]byte, error) { + rsaKey, ok := key.handle.(*rsa.PrivateKey) + if !ok { + return nil, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPrivateKey, "RSA", key.handle)) + } + + hash, err := extractHashFromRSAKey(key) + if err != nil { + return nil, err + } + + hashedData := hash.New() + hashedData.Write(data) + + signature, err := rsa.SignPSS(rand.Reader, rsaKey, hash, hashedData.Sum(nil), &rsa.PSSOptions{ + SaltLength: rsasv.SaltLength, + }) + if err != nil { + return nil, NewError(OperationError, "could not sign data: "+err.Error()) + } + + return signature, nil +} + +// Verify verifies the signature of the given data. +func (rsasv *RSAPssParams) Verify(key CryptoKey, signature []byte, data []byte) (bool, error) { + rsaKey, ok := key.handle.(*rsa.PublicKey) + if !ok { + return false, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPublicKey, "RSA", key.handle)) + } + + hash, err := extractHashFromRSAKey(key) + if err != nil { + return false, err + } + + hashedData := hash.New() + hashedData.Write(data) + + err = rsa.VerifyPSS(rsaKey, hash, hashedData.Sum(nil), signature, &rsa.PSSOptions{ + SaltLength: rsasv.SaltLength, + }) + return err == nil, nil +} + +// Encrypt . +func (rsaoaep *RSAOaepParams) Encrypt(plaintext []byte, key CryptoKey) ([]byte, error) { + rsaKey, ok := key.handle.(*rsa.PublicKey) + if !ok { + return nil, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPublicKey, "RSA", key.handle)) + } + + hash, err := extractHashFromRSAKey(key) + if err != nil { + return nil, err + } + + ciphertext, err := rsa.EncryptOAEP(hash.New(), rand.Reader, rsaKey, plaintext, rsaoaep.Label) + if err != nil { + return nil, NewError(OperationError, "could not encrypt data: "+err.Error()) + } + + return ciphertext, nil +} + +// Decrypt . +func (rsaoaep *RSAOaepParams) Decrypt(ciphertext []byte, key CryptoKey) ([]byte, error) { + rsaKey, ok := key.handle.(*rsa.PrivateKey) + if !ok { + return nil, NewError(InvalidAccessError, fmt.Sprintf(errMsgNotExpectedPrivateKey, "RSA", key.handle)) + } + + hash, err := extractHashFromRSAKey(key) + if err != nil { + return nil, err + } + + plaintext, err := rsa.DecryptOAEP(hash.New(), rand.Reader, rsaKey, ciphertext, rsaoaep.Label) + if err != nil { + return nil, NewError(OperationError, "could not decrypt data: "+err.Error()) + } + + return plaintext, nil +} diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/signer.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/signer.go index 3f8b92f542d..98a8257f7b9 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/signer.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/signer.go @@ -14,6 +14,10 @@ func newSignerVerifier(rt *sobek.Runtime, normalized Algorithm, params sobek.Val return &hmacSignerVerifier{}, nil case ECDSA: return newECDSAParams(rt, normalized, params) + case RSASsaPkcs1v15: + return &rsaSsaPkcs1v15SignerVerifier{}, nil + case RSAPss: + return newRSAPssParams(rt, normalized, params) default: return nil, NewError(NotSupportedError, "unsupported algorithm for signing/verifying: "+normalized.Name) } diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/goja.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/sobek.go similarity index 100% rename from vendor/github.com/grafana/xk6-webcrypto/webcrypto/goja.go rename to vendor/github.com/grafana/xk6-webcrypto/webcrypto/sobek.go diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/subtle_crypto.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/subtle_crypto.go index 7f9026bc358..9627656d649 100644 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/subtle_crypto.go +++ b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/subtle_crypto.go @@ -40,7 +40,7 @@ type SubtleCrypto struct { // The `data` parameter should contain the data to be encryption. func (sc *SubtleCrypto) Encrypt( //nolint:dupl // we have two similar methods algorithm, key, data sobek.Value, -) *sobek.Promise { +) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -89,8 +89,8 @@ func (sc *SubtleCrypto) Encrypt( //nolint:dupl // we have two similar methods promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -99,16 +99,14 @@ func (sc *SubtleCrypto) Encrypt( //nolint:dupl // we have two similar methods callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(rt.NewArrayBuffer(result)) - return nil + return resolve(rt.NewArrayBuffer(result)) }) }() - return promise + return promise, nil } // Decrypt decrypts some encrypted data. @@ -133,7 +131,7 @@ func (sc *SubtleCrypto) Encrypt( //nolint:dupl // we have two similar methods // The `data` parameter should contain the data to be decrypted. func (sc *SubtleCrypto) Decrypt( //nolint:dupl // we have two similar methods algorithm, key, data sobek.Value, -) *sobek.Promise { +) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -181,8 +179,8 @@ func (sc *SubtleCrypto) Decrypt( //nolint:dupl // we have two similar methods promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -191,16 +189,14 @@ func (sc *SubtleCrypto) Decrypt( //nolint:dupl // we have two similar methods callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(rt.NewArrayBuffer(result)) - return nil + return resolve(rt.NewArrayBuffer(result)) }) }() - return promise + return promise, nil } // Sign generates a digital signature. @@ -223,7 +219,7 @@ func (sc *SubtleCrypto) Decrypt( //nolint:dupl // we have two similar methods // `algorithm` identifies a public-key cryptosystem, this is the private key. // // The `data` parameter should contain the data to be signed. -func (sc *SubtleCrypto) Sign(algorithm, key, data sobek.Value) *sobek.Promise { +func (sc *SubtleCrypto) Sign(algorithm, key, data sobek.Value) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -276,8 +272,8 @@ func (sc *SubtleCrypto) Sign(algorithm, key, data sobek.Value) *sobek.Promise { promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -286,16 +282,14 @@ func (sc *SubtleCrypto) Sign(algorithm, key, data sobek.Value) *sobek.Promise { callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(rt.NewArrayBuffer(signature)) - return nil + return resolve(rt.NewArrayBuffer(signature)) }) }() - return promise + return promise, nil } // Verify verifies a digital signature. @@ -321,7 +315,7 @@ func (sc *SubtleCrypto) Sign(algorithm, key, data sobek.Value) *sobek.Promise { // The `signature` parameter should contain the signature to be verified. // // The `data` parameter should contain the original signed data. -func (sc *SubtleCrypto) Verify(algorithm, key, signature, data sobek.Value) *sobek.Promise { +func (sc *SubtleCrypto) Verify(algorithm, key, signature, data sobek.Value) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -367,7 +361,7 @@ func (sc *SubtleCrypto) Verify(algorithm, key, signature, data sobek.Value) *sob } if !ck.ContainsUsage(VerifyCryptoKeyUsage) { - return NewError(InvalidAccessError, "key does not contain the 'sign' usage") + return NewError(InvalidAccessError, "key does not contain the 'verify' usage") } return nil @@ -375,8 +369,8 @@ func (sc *SubtleCrypto) Verify(algorithm, key, signature, data sobek.Value) *sob promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -385,16 +379,14 @@ func (sc *SubtleCrypto) Verify(algorithm, key, signature, data sobek.Value) *sob callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(verified) - return nil + return resolve(verified) }) }() - return promise + return promise, nil } // Digest generates a digest of the given data. @@ -415,7 +407,7 @@ func (sc *SubtleCrypto) Verify(algorithm, key, signature, data sobek.Value) *sob // - SHA-512 // // The `data` parameter should contain the data to be digested. -func (sc *SubtleCrypto) Digest(algorithm sobek.Value, data sobek.Value) *sobek.Promise { +func (sc *SubtleCrypto) Digest(algorithm sobek.Value, data sobek.Value) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -446,7 +438,11 @@ func (sc *SubtleCrypto) Digest(algorithm sobek.Value, data sobek.Value) *sobek.P var ok bool hashFn, ok = getHashFn(normalized.Name) if !ok { - return NewError(NotSupportedError, "unsupported algorithm: "+normalized.Name) + return NewError( + NotSupportedError, + "unsupported digest algorithm '"+normalized.Name+"', "+ + "accepted values are: SHA-1, SHA-256, SHA-384, and SHA-512", + ) } return nil @@ -454,8 +450,8 @@ func (sc *SubtleCrypto) Digest(algorithm sobek.Value, data sobek.Value) *sobek.P promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -466,16 +462,14 @@ func (sc *SubtleCrypto) Digest(algorithm sobek.Value, data sobek.Value) *sobek.P callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(rt.NewArrayBuffer(digest)) - return nil + return resolve(rt.NewArrayBuffer(digest)) }) }() - return promise + return promise, nil } // GenerateKey generate a new key (for symmetric algorithms) or key pair (for public-key algorithms). @@ -498,7 +492,7 @@ func (sc *SubtleCrypto) Digest(algorithm sobek.Value, data sobek.Value) *sobek.P // The `keyUsages` parameter is an array of strings indicating what the key can be used for. func (sc *SubtleCrypto) GenerateKey( algorithm sobek.Value, extractable bool, keyUsages []CryptoKeyUsage, -) *sobek.Promise { +) (*sobek.Promise, error) { rt := sc.vu.Runtime() var keyGenerator KeyGenerator @@ -519,8 +513,8 @@ func (sc *SubtleCrypto) GenerateKey( promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -552,16 +546,14 @@ func (sc *SubtleCrypto) GenerateKey( callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(result) - return nil + return resolve(result) }) }() - return promise + return promise, nil } // DeriveKey can be used to derive a secret key from a master key. @@ -638,7 +630,7 @@ func (sc *SubtleCrypto) DeriveBits( //nolint:funlen,gocognit // we have a lot of algorithm sobek.Value, baseKey sobek.Value, length int, -) *sobek.Promise { +) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -721,8 +713,8 @@ func (sc *SubtleCrypto) DeriveBits( //nolint:funlen,gocognit // we have a lot of promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -746,16 +738,14 @@ func (sc *SubtleCrypto) DeriveBits( //nolint:funlen,gocognit // we have a lot of callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(rt.NewArrayBuffer(result)) - return nil + return resolve(rt.NewArrayBuffer(result)) }) }() - return promise + return promise, nil } // ImportKey imports a key: that is, it takes as input a key in an external, portable @@ -782,7 +772,7 @@ func (sc *SubtleCrypto) ImportKey( //nolint:funlen // we have a lot of error han algorithm sobek.Value, extractable bool, keyUsages []CryptoKeyUsage, -) *sobek.Promise { +) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -824,8 +814,8 @@ func (sc *SubtleCrypto) ImportKey( //nolint:funlen // we have a lot of error han promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -851,16 +841,14 @@ func (sc *SubtleCrypto) ImportKey( //nolint:funlen // we have a lot of error han callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } - resolve(result) - return nil + return resolve(result) }) }() - return promise + return promise, nil } // ExportKey exports a key: that is, it takes as input a CryptoKey object and gives @@ -880,7 +868,7 @@ func (sc *SubtleCrypto) ImportKey( //nolint:funlen // we have a lot of error han func (sc *SubtleCrypto) ExportKey( //nolint:funlen // we have a lot of error handling format KeyFormat, key sobek.Value, -) *sobek.Promise { +) (*sobek.Promise, error) { rt := sc.vu.Runtime() var ( @@ -921,6 +909,8 @@ func (sc *SubtleCrypto) ExportKey( //nolint:funlen // we have a lot of error han keyExporter = exportHMACKey case ECDH, ECDSA: keyExporter = exportECKey + case RSASsaPkcs1v15, RSAOaep, RSAPss: + keyExporter = exportRSAKey default: return NewError(NotSupportedError, "unsupported algorithm "+algorithm.Name) } @@ -930,8 +920,8 @@ func (sc *SubtleCrypto) ExportKey( //nolint:funlen // we have a lot of error han promise, resolve, reject := rt.NewPromise() if err != nil { - reject(err) - return promise + err := reject(err) + return promise, err } callback := sc.vu.RegisterCallback() @@ -940,27 +930,23 @@ func (sc *SubtleCrypto) ExportKey( //nolint:funlen // we have a lot of error han callback(func() error { if err != nil { - reject(err) - return nil //nolint:nilerr // we return nil to indicate that the error was handled + return reject(err) } if !isBinaryExportedFormat(format) { - resolve(result) - return nil + return resolve(result) } b, ok := result.([]byte) if !ok { - reject(NewError(ImplementationError, "for "+format+" []byte expected as result")) - return nil + return reject(NewError(ImplementationError, "for "+format+" []byte expected as result")) } - resolve(rt.NewArrayBuffer(b)) - return nil + return resolve(rt.NewArrayBuffer(b)) }) }() - return promise + return promise, nil } func isBinaryExportedFormat(format KeyFormat) bool { @@ -1001,9 +987,9 @@ func (sc *SubtleCrypto) WrapKey( key sobek.Value, wrappingKey sobek.Value, wrapAlgorithm sobek.Value, -) *sobek.Promise { +) (*sobek.Promise, error) { // TODO: implementation - return nil + return nil, errors.New("not implemented") } // UnwrapKey "unwraps" a key. @@ -1059,7 +1045,7 @@ func (sc *SubtleCrypto) UnwrapKey( unwrappedKeyAlgo sobek.Value, extractable bool, keyUsages []CryptoKeyUsage, -) *sobek.Promise { +) (*sobek.Promise, error) { // TODO: implementation - return nil + return nil, errors.New("not implemented") } diff --git a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/test_setup.go b/vendor/github.com/grafana/xk6-webcrypto/webcrypto/test_setup.go deleted file mode 100644 index 84d39770cf6..00000000000 --- a/vendor/github.com/grafana/xk6-webcrypto/webcrypto/test_setup.go +++ /dev/null @@ -1,102 +0,0 @@ -package webcrypto - -import ( - "io" - "os" - "path" - "path/filepath" - "testing" - - "go.k6.io/k6/js/compiler" - - "github.com/grafana/sobek" - "github.com/stretchr/testify/require" - k6encoding "go.k6.io/k6/js/modules/k6/encoding" - "go.k6.io/k6/js/modulestest" -) - -const initGlobals = ` - globalThis.CryptoKey = require("k6/x/webcrypto").CryptoKey; -` - -// newConfiguredRuntime initializes a new test setup. -// It prepares a test setup with a mocked redis server and a goja runtime, -// and event loop, ready to execute scripts as if being executed in the -// main context of k6. -func newConfiguredRuntime(t testing.TB) *modulestest.Runtime { - var err error - runtime := modulestest.NewRuntime(t) - - err = runtime.SetupModuleSystem( - map[string]interface{}{"k6/x/webcrypto": New()}, - nil, - compiler.New(runtime.VU.InitEnv().Logger), - ) - require.NoError(t, err) - - // We compile the Web Platform testharness script into a sobek.Program - harnessProgram, err := CompileFile("./tests/util", "testharness.js") - require.NoError(t, err) - - // We execute the harness script in the goja runtime - // in order to make the Web Platform assertion functions available - // to the tests. - _, err = runtime.VU.Runtime().RunProgram(harnessProgram) - require.NoError(t, err) - - // We compile the Web Platform helpers script into a sobek.Program - helpersProgram, err := CompileFile("./tests/util", "helpers.js") - require.NoError(t, err) - - // We execute the helpers script in the goja runtime - // in order to make the Web Platform helpers available - // to the tests. - _, err = runtime.VU.Runtime().RunProgram(helpersProgram) - require.NoError(t, err) - - m := new(RootModule).NewModuleInstance(runtime.VU) - - err = runtime.VU.Runtime().Set("crypto", m.Exports().Named["crypto"]) - require.NoError(t, err) - - // we define the btoa function in the goja runtime - // so that the Web Platform tests can use it. - encodingModule := k6encoding.New().NewModuleInstance(runtime.VU) - err = runtime.VU.Runtime().Set("btoa", encodingModule.Exports().Named["b64encode"]) - require.NoError(t, err) - - _, err = runtime.VU.Runtime().RunString(initGlobals) - require.NoError(t, err) - - return runtime -} - -// CompileFile compiles a javascript file as a sobek.Program. -func CompileFile(base, name string) (*sobek.Program, error) { - filename := path.Join(base, name) - - //nolint:forbidigo // Allow os.Open in tests - f, err := os.Open(filepath.Clean(filename)) - if err != nil { - return nil, err - } - defer func() { - err = f.Close() - if err != nil { - panic(err) - } - }() - - b, err := io.ReadAll(f) - if err != nil { - return nil, err - } - - str := string(b) - program, err := sobek.Compile(name, str, false) - if err != nil { - return nil, err - } - - return program, nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 038b8c5e852..1aacf29f906 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -205,8 +205,8 @@ github.com/grafana/xk6-output-prometheus-remote/pkg/stale # github.com/grafana/xk6-redis v0.3.1 ## explicit; go 1.20 github.com/grafana/xk6-redis/redis -# github.com/grafana/xk6-webcrypto v0.4.0 -## explicit; go 1.20 +# github.com/grafana/xk6-webcrypto v0.5.0 +## explicit; go 1.21 github.com/grafana/xk6-webcrypto/webcrypto # github.com/grafana/xk6-websockets v0.7.2 ## explicit; go 1.21