Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for 25519 algos #4556

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ all: clean format tests build
build:
go build

build-debug:
go build -gcflags="all=-N -l"

## format: Applies Go formatting to code.
format:
find . -name '*.go' -exec gofmt -s -w {} +
Expand Down
44 changes: 44 additions & 0 deletions examples/webcrypto/sign_verify/sign-verify-ed25519.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { crypto } from "k6/experimental/webcrypto";

export default async function () {
const keyPair = await crypto.subtle.generateKey(
{
name: "Ed25519",
},
true,
["sign", "verify"]
);

const data = string2ArrayBuffer("Hello World");

const alg = { name: "Ed25519" };

// makes a signature of the encoded data with the provided key
const signature = await crypto.subtle.sign(alg, keyPair.privateKey, data);

console.log("signature: ", printArrayBuffer(signature));

//Verifies the signature of the encoded data with the provided key
const verified = await crypto.subtle.verify(
alg,
otherKeyPair.publicKey,
signature,
data
);

console.log("verified: ", verified);
}

const string2ArrayBuffer = (str) => {
let buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
let bufView = new Uint16Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
};

const printArrayBuffer = (buffer) => {
let view = new Uint8Array(buffer);
return Array.from(view);
};
10 changes: 7 additions & 3 deletions internal/js/modules/k6/webcrypto/algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@

// ECDH represents the ECDH algorithm.
ECDH = "ECDH"

Ed25519 = "ED25519" // TODO: This should be "Ed25519"

Check failure on line 56 in internal/js/modules/k6/webcrypto/algorithm.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported const Ed25519 should have comment (or a comment on this block) or be unexported (revive)
)

// HashAlgorithmIdentifier represents the name of a hash algorithm.
Expand Down Expand Up @@ -187,16 +189,18 @@
isHashAlgorithm(algorithmName) ||
algorithmName == HMAC ||
isEllipticCurve(algorithmName) ||
isRSAAlgorithm(algorithmName)
isRSAAlgorithm(algorithmName) ||
algorithmName == Ed25519
case OperationIdentifierExportKey, OperationIdentifierImportKey:
return isAesAlgorithm(algorithmName) ||
algorithmName == HMAC ||
isEllipticCurve(algorithmName) ||
isRSAAlgorithm(algorithmName)
isRSAAlgorithm(algorithmName) ||
algorithmName == Ed25519
case OperationIdentifierEncrypt, OperationIdentifierDecrypt:
return isAesAlgorithm(algorithmName) || algorithmName == RSAOaep
case OperationIdentifierSign, OperationIdentifierVerify:
return algorithmName == HMAC || algorithmName == ECDSA || algorithmName == RSAPss || algorithmName == RSASsaPkcs1v15
return algorithmName == HMAC || algorithmName == ECDSA || algorithmName == RSAPss || algorithmName == RSASsaPkcs1v15 || algorithmName == Ed25519

Check failure on line 203 in internal/js/modules/k6/webcrypto/algorithm.go

View workflow job for this annotation

GitHub Actions / lint

The line is 146 characters long, which exceeds the maximum of 120 characters. (lll)
default:
return false
}
Expand Down
102 changes: 102 additions & 0 deletions internal/js/modules/k6/webcrypto/ed25519.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package webcrypto

import (
"crypto/ed25519"
"crypto/rand"
"fmt"
)

// Ed25519KeyGenParams represents the object that should be passed as the algorithm
// paramter into `SubtleCrypto.GenerateKey`, when generating an Ed25519 key pair.

Check failure on line 10 in internal/js/modules/k6/webcrypto/ed25519.go

View workflow job for this annotation

GitHub Actions / lint

`paramter` is a misspelling of `parameter` (misspell)
// The Ed25519 key generation expects only the algorithm type as a parameter.
type Ed25519KeyGenParams struct {
Algorithm
}

var _ KeyGenerator = &Ed25519KeyGenParams{}

func newEd25519KeyGenParams(normalized Algorithm) (KeyGenerator, error) {

Check failure on line 18 in internal/js/modules/k6/webcrypto/ed25519.go

View workflow job for this annotation

GitHub Actions / lint

newEd25519KeyGenParams - result 1 (error) is always nil (unparam)
return &Ed25519KeyGenParams{
Algorithm: normalized,
}, nil
}

func (kgp *Ed25519KeyGenParams) GenerateKey(extractable bool, keyUsages []CryptoKeyUsage) (CryptoKeyGenerationResult, error) {

Check failure on line 24 in internal/js/modules/k6/webcrypto/ed25519.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported method Ed25519KeyGenParams.GenerateKey should have comment or be unexported (revive)
rawPublicKey, rawPrivateKey, err := generateEd25519KeyPair(keyUsages)
if err != nil {
return nil, err
}

alg := KeyAlgorithm{
Algorithm: kgp.Algorithm,
}
privateKey := &CryptoKey{
Type: PrivateCryptoKeyType,
Extractable: extractable,
Algorithm: alg,
Usages: UsageIntersection(
keyUsages,
[]CryptoKeyUsage{SignCryptoKeyUsage},
),
handle: rawPrivateKey,
}

publicKey := &CryptoKey{
Type: PublicCryptoKeyType,
Extractable: true,
Algorithm: alg,
Usages: UsageIntersection(
keyUsages,
[]CryptoKeyUsage{VerifyCryptoKeyUsage},
),
handle: rawPublicKey,
}

return &CryptoKeyPair{
PrivateKey: privateKey,
PublicKey: publicKey,
}, nil
}

func generateEd25519KeyPair(keyUsages []CryptoKeyUsage) (ed25519.PublicKey, ed25519.PrivateKey, error) {
for _, usage := range keyUsages {
switch usage {
case SignCryptoKeyUsage, VerifyCryptoKeyUsage:
continue
default:
return nil, nil, NewError(SyntaxError, fmt.Sprintf("Invalid key usage: %s", usage))
}
}

return ed25519.GenerateKey(rand.Reader)
}

type ed25519SignerVerifier struct{}

func (ed25519SignerVerifier) Sign(key CryptoKey, data []byte) ([]byte, error) {
if key.Type != PrivateCryptoKeyType {
return nil, NewError(InvalidAccessError, "Must use private key to sign data")
}

keyHandle, ok := key.handle.(ed25519.PrivateKey)
if !ok {
return nil, NewError(InvalidAccessError, "Key handle is not an Ed25519 Private Key")
}

return ed25519.Sign(keyHandle, data), nil
}

func (ed25519SignerVerifier) Verify(key CryptoKey, signature, data []byte) (bool, error) {
if key.Type != PublicCryptoKeyType {
return false, NewError(InvalidAccessError, "Must use public key to verify data")
}

keyHandle, ok := key.handle.(ed25519.PublicKey)
if !ok {
return false, NewError(InvalidAccessError, "Key handle is not an Ed25519 public key")
}

// TODO: verify that the ed25519 library conducts small-order checks, if not add them here

return ed25519.Verify(keyHandle, data, signature), nil
}
4 changes: 3 additions & 1 deletion internal/js/modules/k6/webcrypto/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,10 @@
kg, err = newECKeyGenParams(rt, normalized, params)
case RSASsaPkcs1v15, RSAPss, RSAOaep:
kg, err = newRsaHashedKeyGenParams(rt, normalized, params)
case Ed25519:
kg, err = newEd25519KeyGenParams(normalized)
default:
validAlgorithms := []string{AESCbc, AESCtr, AESGcm, AESKw, HMAC, ECDH, ECDSA, RSASsaPkcs1v15, RSAPss, RSAOaep}
validAlgorithms := []string{AESCbc, AESCtr, AESGcm, AESKw, HMAC, ECDH, ECDSA, RSASsaPkcs1v15, RSAPss, RSAOaep, Ed25519}

Check failure on line 197 in internal/js/modules/k6/webcrypto/key.go

View workflow job for this annotation

GitHub Actions / lint

The line is 121 characters long, which exceeds the maximum of 120 characters. (lll)
return nil, NewError(
NotImplemented,
"unsupported key generation algorithm '"+normalized.Name+"', "+
Expand Down
2 changes: 2 additions & 0 deletions internal/js/modules/k6/webcrypto/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ func newSignerVerifier(rt *sobek.Runtime, normalized Algorithm, params sobek.Val
return &rsaSsaPkcs1v15SignerVerifier{}, nil
case RSAPss:
return newRSAPssParams(rt, normalized, params)
case Ed25519:
return &ed25519SignerVerifier{}, nil
default:
return nil, NewError(NotSupportedError, "unsupported algorithm for signing/verifying: "+normalized.Name)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
diff --git a/WebCryptoAPI/generateKey/failures.js b/WebCryptoAPI/generateKey/failures.js
index e0f0279a6..61495ca75 100644
index e0f0279a6..5b3b69764 100644
--- a/WebCryptoAPI/generateKey/failures.js
+++ b/WebCryptoAPI/generateKey/failures.js
@@ -32,10 +32,10 @@ function run_test(algorithmNames) {
{name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]},
@@ -33,9 +33,9 @@ function run_test(algorithmNames) {
{name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
{name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
- {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
{name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
- {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
- {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
- {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+ // {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+ // {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+ // {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
{name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
- {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+ // {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/WebCryptoAPI/generateKey/successes.js b/WebCryptoAPI/generateKey/successes.js
index a9a168e1a..88861ab87 100644
index a9a168e1a..e3976c378 100644
--- a/WebCryptoAPI/generateKey/successes.js
+++ b/WebCryptoAPI/generateKey/successes.js
@@ -21,17 +21,17 @@ function run_test(algorithmNames, slowTest) {
Expand All @@ -16,11 +16,10 @@ index a9a168e1a..88861ab87 100644
{name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
- {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
- {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
- {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
- {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+ // {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+ {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign", "wrapKey"]},
+ // {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+ // {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
{name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
- {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+ // {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
];

Expand Down
Loading