Skip to content

Implement Elliptic Curve (EC) crypto support#3038

Open
valientegaston wants to merge 4 commits into
toitlang:masterfrom
valientegaston:ec-support
Open

Implement Elliptic Curve (EC) crypto support#3038
valientegaston wants to merge 4 commits into
toitlang:masterfrom
valientegaston:ec-support

Conversation

@valientegaston
Copy link
Copy Markdown
Contributor

Adds EC key generation, ECDSA signing/verification, ECDH key exchange, and ECIES encryption to the crypto module.

What's included

  • EcKeyPair / EcKey classes for key management and signing
  • Ecies class for encryption/decryption using ECDH + HKDF-SHA256 + AES-128-GCM
  • Backend primitives for key generation, ECDSA operations, and shared secret computation
  • Full DER import/export support for keys
  • Supported curves: secp256r1 (default), secp384r1, secp521r1

Copy link
Copy Markdown
Member

@floitsch floitsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round :)
Looks good though. Nothing serious.

Comment thread lib/crypto/ec.toit Outdated

/**
Generates a new EC key pair for the given $curve.
Common curves: "secp256r1", "secp384r1", "secp521r1".
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I know which curves are available?
Should we make constants for them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Added named constants to EcKey:

static CURVE-SECP256R1 ::= "secp256r1"
static CURVE-SECP384R1 ::= "secp384r1"
static CURVE-SECP521R1 ::= "secp521r1"

The generate docstring now references them as well.

Comment thread lib/crypto/ec.toit Outdated
The $password is optional and used for encrypted keys.
*/
static parse-private key/io.Data --password/string?="" -> EcKey:
der := ec-get-private-key-der_ key (password or "")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value already ensures that it is never null.

Suggested change
der := ec-get-private-key-der_ key (password or "")
der := ec-get-private-key-der_ key password

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment thread lib/crypto/ec.toit Outdated
The $key must be DER or PEM.
The $password is optional and used for encrypted keys.
*/
static parse-private key/io.Data --password/string?="" -> EcKey:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static parse-private key/io.Data --password/string?="" -> EcKey:
static parse-private key/io.Data --password/string="" -> EcKey:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment thread lib/crypto/hkdf.toit Outdated
The $ikm is the input keying material.
The $salt is optional.
The $info is optional context information.
The $length is the desired length of the output key.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of Toit code uses 'size' instead of 'length'. Maybe the crypto libs are different.
have a look at the existing code and decide by yourself.
I guess the length is in bytes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the existing crypto code — size is the predominant convention. Renamed --length to --size throughout and explicitly documented that it is in bytes.

Comment thread lib/crypto/hkdf.toit Outdated
The $hasher-creator is a lambda that creates an HMAC object for a given key.
*/
hkdf --ikm/ByteArray --salt/ByteArray? --info/ByteArray? --length/int [hasher-creator] -> ByteArray:
// Step 1: Extract
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Step 1: Extract

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Comment thread src/primitive_crypto.cc
return result;
}

PRIMITIVE(ec_get_public_key_der) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very similar to ec_get_private_key_der. Can we use a shared helper to avoid the code duplication?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Extracted ec_get_der_helper as a shared static helper, modeled after the existing rsa_export_der pattern. Both ec_get_private_key_der and ec_get_public_key_der are now thin wrappers around it.

Comment thread src/primitive_crypto.cc Outdated
}

size_t glen = (ec_prv->grp.pbits + 7) / 8;
ByteArray* result = process->allocate_byte_array(glen);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the allocation to the top of the function.

Comment thread tests/ec_test.toit Outdated
curves.do: | curve |
print "Testing curve: $curve"

// Test generation
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finish comments with ".". Here and in the rest of the file.

Suggested change
// Test generation
// Test generation.

Comment thread tests/ec_test.toit Outdated
print " Signature: $(signature.size) bytes"

// Test verification
if key-pair.verify message signature:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically just write expect (key-pair.verify message signature). But i can live with the verbose output.

Comment thread tests/ec_test.toit Outdated
// 1. Tamper with tag (last bytes)
tampered-tag := ciphertext.copy
tampered-tag[tampered-tag.size - 1] ^= 0xFF
if (catch: key-pair.decrypt-ecies tampered-tag) != ec.Ecies.AUTHENTICATION-FAILURE:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect-throws would deal with that.
here and below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fixed!

  • Terminated all comments with . throughout the file.
  • Replaced the verbose if/else verification pattern with expect (key-pair.verify message signature).
  • Replaced all catch: ... != AUTHENTICATION-FAILURE patterns with expect-throws.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants