Skip to content

Commit

Permalink
Merge pull request #56 from grafana/fix/55
Browse files Browse the repository at this point in the history
Fix #55 and drop usage of `goja.SetTagFieldMapper` in the module code
  • Loading branch information
oleiade authored Jan 12, 2024
2 parents 1ff651f + 95b931a commit 1ec0d84
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 33 deletions.
18 changes: 9 additions & 9 deletions webcrypto/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type AESKeyGenParams struct {
Algorithm

// The length, in bits, of the key.
Length bitLength `json:"length"`
Length bitLength `js:"length"`
}

// newAESKeyGenParams creates a new AESKeyGenParams object, from the
Expand Down Expand Up @@ -111,7 +111,7 @@ var _ KeyGenerator = &AESKeyGenParams{}
type AESKeyAlgorithm struct {
Algorithm

Length int64 `json:"length"`
Length int64 `js:"length"`
}

// exportAESKey exports an AES key to its raw representation.
Expand Down Expand Up @@ -213,12 +213,12 @@ type AESCBCParams struct {
Algorithm

// Name should be set to AES-CBC.
Name string `json:"name"`
Name string `js:"name"`

// Iv holds (an ArrayBuffer, a TypedArray, or a DataView) the initialization vector.
// Must be 16 bytes, unpredictable, and preferably cryptographically random.
// However, it need not be secret (for example, it may be transmitted unencrypted along with the ciphertext).
Iv []byte `json:"iv"`
Iv []byte `js:"iv"`
}

// Encrypt encrypts the given plaintext using the AES-CBC algorithm, and returns the ciphertext.
Expand Down Expand Up @@ -317,15 +317,15 @@ type AESCTRParams struct {
//
// For example, if length is set to 64, then the first half of counter is
// the nonce and the second half is used for the counter.
Counter []byte `json:"counter"`
Counter []byte `js:"counter"`

// Length holds (a Number) the number of bits in the counter block that are used for the actual counter.
// The counter must be big enough that it doesn't wrap: if the message is n blocks and the counter is m bits long, then
// the following must be true: n <= 2^m.
//
// The NIST SP800-38A standard, which defines CTR, suggests that the counter should occupy half of the counter
// block (see Appendix B.2), so for AES it would be 64.
Length int `json:"length"`
Length int `js:"length"`
}

// Encrypt encrypts the given plaintext using the AES-CTR algorithm, and returns the ciphertext.
Expand Down Expand Up @@ -417,7 +417,7 @@ type AESGCMParams struct {
// Section 8.2 of the specification outlines methods for constructing IVs.
// Note that the IV does not have to be secret, just unique: so it is OK, for example, to
// transmit it in the clear alongside the encrypted message.
Iv []byte `json:"iv"`
Iv []byte `js:"iv"`

// AdditionalData (an ArrayBuffer, a TypedArray, or a DataView) contains additional data that will
// not be encrypted but will be authenticated along with the encrypted data.
Expand All @@ -431,7 +431,7 @@ type AESGCMParams struct {
//
// The additionalData property is optional and may be omitted without compromising the
// security of the encryption operation.
AdditionalData []byte `json:"additionalData"`
AdditionalData []byte `js:"additionalData"`

// TagLength (a Number) determines the size in bits of the authentication tag generated in
// the encryption operation and used for authentication in the corresponding decryption.
Expand All @@ -443,7 +443,7 @@ type AESGCMParams struct {
// in some applications: Appendix C of the specification provides additional guidance here.
//
// tagLength is optional and defaults to 128 if it is not specified.
TagLength bitLength `json:"tagLength"`
TagLength bitLength `js:"tagLength"`
}

// Encrypt encrypts the given plaintext using the AES-GCM algorithm, and returns the ciphertext.
Expand Down
2 changes: 1 addition & 1 deletion webcrypto/algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

// Algorithm represents
type Algorithm struct {
Name AlgorithmIdentifier `json:"name"`
Name AlgorithmIdentifier `js:"name"`
}

// AlgorithmIdentifier represents the name of an algorithm.
Expand Down
4 changes: 2 additions & 2 deletions webcrypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
type Crypto struct {
vu modules.VU

Subtle *SubtleCrypto `json:"subtle"`
CryptoKey *CryptoKey `json:"CryptoKey"`
Subtle *SubtleCrypto `js:"subtle"`
CryptoKey *CryptoKey `js:"CryptoKey"`
}

// GetRandomValues lets you get cryptographically strong random values.
Expand Down
4 changes: 2 additions & 2 deletions webcrypto/elliptic_curve.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ package webcrypto
// key pair: that is, when the algorithm is identified as either of ECDSA or ECDH.
type EcKeyImportParams struct {
// Name should be set to AlgorithmKindEcdsa or AlgorithmKindEcdh.
Name AlgorithmIdentifier `json:"name"`
Name AlgorithmIdentifier `js:"name"`

// NamedCurve holds (a String) the name of the elliptic curve to use.
NamedCurve EllipticCurveKind `json:"namedCurve"`
NamedCurve EllipticCurveKind `js:"namedCurve"`
}

// EllipticCurveKind represents the kind of elliptic curve that is being used.
Expand Down
4 changes: 2 additions & 2 deletions webcrypto/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ const (
// Web Crypto API.
type Error struct {
// Name contains one of the strings associated with an error name.
Name string `json:"name"`
Name string `js:"name"`

// Message represents message or description associated with the given error name.
Message string `json:"message"`
Message string `js:"message"`
}

// Error implements the `error` interface, so WebCryptoError are normal Go errors.
Expand Down
12 changes: 6 additions & 6 deletions webcrypto/hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ type HMACKeyGenParams struct {
// Hash represents the name of the digest function to use. You can
// use any of the following: [Sha256], [Sha384],
// or [Sha512].
Hash Algorithm `json:"hash"`
Hash Algorithm `js:"hash"`

// Length holds (a Number) the length of the key, in bits.
// If this is omitted, the length of the key is equal to the block size
// of the hash function you have chosen.
// Unless you have a good reason to use a different length, omit
// use the default.
Length null.Int `json:"length"`
Length null.Int `js:"length"`
}

// newHMACKeyGenParams creates a new HMACKeyGenParams object, from the normalized
Expand Down Expand Up @@ -163,10 +163,10 @@ type HMACKeyAlgorithm struct {
KeyAlgorithm

// Hash represents the inner hash function to use.
Hash KeyAlgorithm `json:"hash"`
Hash KeyAlgorithm `js:"hash"`

// Length represents he length (in bits) of the key.
Length int64 `json:"length"`
Length int64 `js:"length"`
}

func exportHMACKey(ck *CryptoKey, format KeyFormat) ([]byte, error) {
Expand Down Expand Up @@ -209,14 +209,14 @@ type HMACImportParams struct {
// Hash represents the name of the digest function to use. You can
// use any of the following: [Sha256], [Sha384],
// or [Sha512].
Hash Algorithm `json:"hash"`
Hash Algorithm `js:"hash"`

// Length holds (a Number) the length of the key, in bits.
// If this is omitted, the length of the key is equal to the block size
// of the hash function you have chosen.
// Unless you have a good reason to use a different length, omit
// use the default.
Length null.Int `json:"length"`
Length null.Int `js:"length"`
}

// newHMACImportParams creates a new HMACImportParams object from the given
Expand Down
117 changes: 107 additions & 10 deletions webcrypto/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
package webcrypto

import (
"fmt"

"github.com/dop251/goja"
"go.k6.io/k6/js/common"
"go.k6.io/k6/js/modules"
)

Expand All @@ -14,8 +17,6 @@ type (
// ModuleInstance represents an instance of the JS module.
ModuleInstance struct {
vu modules.VU

*Crypto
}
)

Expand All @@ -33,22 +34,118 @@ func New() *RootModule {
// NewModuleInstance implements the modules.Module interface and returns
// a new instance for each VU.
func (*RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
vu.Runtime().SetFieldNameMapper(goja.TagFieldNameMapper("json", true))

return &ModuleInstance{
vu: vu,
Crypto: &Crypto{
vu: vu,
Subtle: &SubtleCrypto{vu: vu},
CryptoKey: &CryptoKey{},
},
}
}

// Exports implements the modules.Instance interface and returns
// the exports of the JS module.
func (mi *ModuleInstance) Exports() modules.Exports {
return modules.Exports{Named: map[string]interface{}{
"crypto": mi.Crypto,
"crypto": newCryptoObject(mi.vu),
}}
}

func newCryptoObject(vu modules.VU) *goja.Object {
rt := vu.Runtime()

obj := rt.NewObject()

crypto := &Crypto{
vu: vu,
Subtle: &SubtleCrypto{vu: vu},
CryptoKey: &CryptoKey{},
}

if err := setReadOnlyPropertyOf(obj, "getRandomValues", rt.ToValue(crypto.GetRandomValues)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "randomUUID", rt.ToValue(crypto.RandomUUID)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "subtle", rt.ToValue(newSubtleCryptoObject(vu))); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "CryptoKey", rt.ToValue(crypto.CryptoKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

return obj
}

func newSubtleCryptoObject(vu modules.VU) *goja.Object {
rt := vu.Runtime()

obj := rt.NewObject()

subtleCrypto := &SubtleCrypto{vu: vu}

if err := setReadOnlyPropertyOf(obj, "decrypt", rt.ToValue(subtleCrypto.Decrypt)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "deriveBits", rt.ToValue(subtleCrypto.DeriveBits)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "deriveKey", rt.ToValue(subtleCrypto.DeriveKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "digest", rt.ToValue(subtleCrypto.Digest)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "encrypt", rt.ToValue(subtleCrypto.Encrypt)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "exportKey", rt.ToValue(subtleCrypto.ExportKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "generateKey", rt.ToValue(subtleCrypto.GenerateKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "importKey", rt.ToValue(subtleCrypto.ImportKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "sign", rt.ToValue(subtleCrypto.Sign)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "unwrapKey", rt.ToValue(subtleCrypto.UnwrapKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "verify", rt.ToValue(subtleCrypto.Verify)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

if err := setReadOnlyPropertyOf(obj, "wrapKey", rt.ToValue(subtleCrypto.WrapKey)); err != nil {
common.Throw(rt, NewError(ImplementationError, err.Error()))
}

return obj
}

// setReadOnlyPropertyOf sets a read-only property on the given [goja.Object].
func setReadOnlyPropertyOf(obj *goja.Object, name string, value goja.Value) error {
err := obj.DefineDataProperty(name,
value,
goja.FLAG_FALSE,
goja.FLAG_FALSE,
goja.FLAG_TRUE,
)
if err != nil {
return fmt.Errorf("unable to define %s read-only property on TextEncoder object; reason: %w", name, err)
}

return nil
}
2 changes: 1 addition & 1 deletion webcrypto/test_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func newTestSetup(t testing.TB) testSetup {
tb := httpmultibin.NewHTTPMultiBin(t)

rt := goja.New()
rt.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
rt.SetFieldNameMapper(common.FieldNameMapper{})

// We compile the Web Platform testharness script into a goja.Program
harnessProgram, err := CompileFile("./tests/util", "testharness.js")
Expand Down

0 comments on commit 1ec0d84

Please sign in to comment.