Skip to content

Commit

Permalink
Add ED25519 ACVP Testing (#1818)
Browse files Browse the repository at this point in the history
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.
  • Loading branch information
skmcgrail authored Sep 12, 2024
1 parent 3f4d2f6 commit 307898a
Show file tree
Hide file tree
Showing 12 changed files with 433 additions and 4 deletions.
7 changes: 7 additions & 0 deletions crypto/fipsmodule/curve25519/curve25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ int ED25519_verify(const uint8_t *message, size_t message_len,
return res;
}

int ED25519_check_public_key(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
return ed25519_check_public_key_s2n_bignum(public_key);
#else
return ed25519_check_public_key_nohw(public_key);
#endif
}

void X25519_public_from_private(
uint8_t out_public_value[X25519_PUBLIC_VALUE_LEN],
Expand Down
8 changes: 8 additions & 0 deletions crypto/fipsmodule/curve25519/curve25519_nohw.c
Original file line number Diff line number Diff line change
Expand Up @@ -2043,3 +2043,11 @@ int ed25519_verify_nohw(uint8_t R_computed_encoded[32],

return 1;
}

int ed25519_check_public_key_nohw(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
ge_p3 A;
if (!x25519_ge_frombytes_vartime(&A, public_key)) {
return 0;
}
return 1;
}
8 changes: 8 additions & 0 deletions crypto/fipsmodule/curve25519/curve25519_s2n_bignum_asm.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,12 @@ int ed25519_verify_s2n_bignum(uint8_t R_computed_encoded[32],
return 1;
}

int ed25519_check_public_key_s2n_bignum(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
uint64_t A[8] = {0};
if (edwards25519_decode_selector(A, public_key) != 0) {
return 0;
}
return 1;
}

#endif
5 changes: 5 additions & 0 deletions crypto/fipsmodule/curve25519/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ void ed25519_sha512(uint8_t out[SHA512_DIGEST_LENGTH],
const void *input1, size_t len1, const void *input2, size_t len2,
const void *input3, size_t len3);


int ed25519_check_public_key_s2n_bignum(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]);
int ed25519_check_public_key_nohw(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]);
OPENSSL_EXPORT int ED25519_check_public_key(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]);

#if defined(__cplusplus)
} // extern C
#endif
Expand Down
308 changes: 308 additions & 0 deletions util/fipstools/acvp/acvptool/subprocess/eddsa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

package subprocess

import (
"encoding/json"
"fmt"
"strings"
)

// NIST ACVP EDDSA Schema: https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html
type eddsa struct{}

func (e *eddsa) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var vs struct {
Mode string `json:"mode"`
TestGroups json.RawMessage `json:"testGroups"`
}

if err := json.Unmarshal(vectorSet, &vs); err != nil {
return nil, err
}

var processTestGroups func(json.RawMessage, Transactable) (interface{}, error)

switch {
case strings.EqualFold(vs.Mode, "keyGen"):
processTestGroups = processEddsaKeyGenTestGroup
case strings.EqualFold(vs.Mode, "keyVer"):
processTestGroups = processEddsaKeyVerTestGroup
case strings.EqualFold(vs.Mode, "sigGen"):
processTestGroups = processEddsaSigGenTestGroup
case strings.EqualFold(vs.Mode, "sigVer"):
processTestGroups = processEddsaSigVerTestGroup
default:
return nil, fmt.Errorf("unsupported EDDSA mode %q", vs.Mode)
}

return processTestGroups(vs.TestGroups, m)
}

func processEddsaKeyGenTestGroup(testGroups json.RawMessage, m Transactable) (interface{}, error) {
var groups []eddsaKeyGenTestGroup
if err := json.Unmarshal(testGroups, &groups); err != nil {
return nil, err
}

var ret []eddsaKeyGenTestGroupResponse

for _, group := range groups {
response := eddsaKeyGenTestGroupResponse{
ID: group.ID,
}

if group.Type != "AFT" {
return nil, fmt.Errorf("unsupported test type %q", group.Type)
}

for _, test := range group.Tests {
result, err := m.Transact("EDDSA/"+string(group.Curve)+"/keyGen", 2)
if err != nil {
return nil, err
}

response.Tests = append(response.Tests, eddsaKeyGenTestCaseResponse{
ID: test.ID,
D: result[0],
Q: result[1],
})
}

ret = append(ret, response)
}

return ret, nil
}

func processEddsaKeyVerTestGroup(testGroups json.RawMessage, m Transactable) (interface{}, error) {
var groups []eddsaKeyVerTestGroup
if err := json.Unmarshal(testGroups, &groups); err != nil {
return nil, err
}

var ret []eddsaKeyVerTestGroupResponse

for _, group := range groups {
if group.Type != "AFT" {
return nil, fmt.Errorf("unsupported test type %q", group.Type)
}

response := eddsaKeyVerTestGroupResponse{
ID: group.ID,
}

for _, test := range group.Tests {
results, err := m.Transact("EDDSA/"+string(group.Curve)+"/keyVer", 1, test.Q)
if err != nil {
return nil, err
}

var passed *bool
if len(results[0]) == 1 {
val := results[0][0] == 1
passed = &val
}

response.Tests = append(response.Tests, eddsaKeyVerTestCaseResponse{
ID: test.ID,
Passed: passed,
})
}

ret = append(ret, response)
}

return ret, nil
}

func processEddsaSigGenTestGroup(testGroups json.RawMessage, m Transactable) (interface{}, error) {
var groups []eddsaSigGenTestGroup
if err := json.Unmarshal(testGroups, &groups); err != nil {
return nil, err
}

var ret []eddsaSigGenTestGroupResponse

for _, group := range groups {
if group.Type != "AFT" && group.Type != "BFT" {
return nil, fmt.Errorf("unsupported test type %q", group.Type)
}

results, err := m.Transact("EDDSA/"+string(group.Curve)+"/keyGen", 2)
if err != nil {
return nil, err
}

seed := results[0]

response := eddsaSigGenTestGroupResponse{
ID: group.ID,
Q: results[1],
}

for _, test := range group.Tests {
results, err := m.Transact("EDDSA/"+string(group.Curve)+"/sigGen", 1, seed, test.Message)
if err != nil {
return nil, err
}

response.Tests = append(response.Tests, eddsaSigGenTestCaseResponse{
ID: test.ID,
Signature: results[0],
})
}

ret = append(ret, response)
}

return ret, nil
}

func processEddsaSigVerTestGroup(testGroups json.RawMessage, m Transactable) (interface{}, error) {
var groups []eddsaSigVerTestGroup
if err := json.Unmarshal(testGroups, &groups); err != nil {
return nil, err
}

var ret []eddsaSigVerTestGroupResponse

for _, group := range groups {
if group.Type != "AFT" {
return nil, fmt.Errorf("unsupported test type %q", group.Type)
}

response := eddsaSigVerTestGroupResponse{
ID: group.ID,
}

for _, test := range group.Tests {
results, err := m.Transact("EDDSA/"+string(group.Curve)+"/sigVer", 1, test.Message, test.Q, test.Signature)
if err != nil {
return nil, err
}

var passed *bool
if len(results[0]) == 1 {
val := results[0][0] == 1
passed = &val
}

response.Tests = append(response.Tests, eddsaSigVerTestCaseResponse{
ID: test.ID,
Passed: passed,
})
}

ret = append(ret, response)
}

return ret, nil
}

const Ed25519 EDDSACurve = "ED-25519"

type EDDSACurve string

func (e *EDDSACurve) UnmarshalJSON(v []byte) error {
var str string

if err := json.Unmarshal(v, &str); err != nil {
return err
}

switch {
case strings.EqualFold(str, "ED-25519"):
*e = Ed25519
default:
return fmt.Errorf("unsupported EDDSA curve: %q", str)
}

return nil
}

type eddsaKeyGenTestGroup struct {
ID uint64 `json:"tgId"`
Curve EDDSACurve `json:"curve"`
Type string `json:"testType"`
Tests []struct {
ID uint64 `json:"tcId"`
}
}

type eddsaKeyVerTestGroup struct {
ID uint64 `json:"tgId"`
Curve EDDSACurve `json:"curve"`
Type string `json:"testType"`
Tests []struct {
ID uint64 `json:"tcId"`
Q hexEncodedByteString `json:"q"`
}
}

type eddsaSigGenTestGroup struct {
ID uint64 `json:"tgId"`
Curve EDDSACurve `json:"curve"`
Prehash bool `json:"prehash"`
Type string `json:"testType"`
Tests []struct {
ID uint64 `json:"tcId"`
Message hexEncodedByteString `json:"message"`
}
}

type eddsaSigVerTestGroup struct {
ID uint64 `json:"tgId"`
Curve EDDSACurve `json:"curve"`
Prehash bool `json:"prehash"`
Type string `json:"testType"`
Tests []struct {
ID uint64 `json:"tcId"`
Message hexEncodedByteString `json:"message"`
Q hexEncodedByteString `json:"q"`
Signature hexEncodedByteString `json:"signature"`
}
}

type eddsaKeyGenTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []eddsaKeyGenTestCaseResponse `json:"tests"`
}

type eddsaKeyGenTestCaseResponse struct {
ID uint64 `json:"tcId"`
D hexEncodedByteString `json:"d"`
Q hexEncodedByteString `json:"q"`
}

type eddsaKeyVerTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []eddsaKeyVerTestCaseResponse `json:"tests"`
}

type eddsaKeyVerTestCaseResponse struct {
ID uint64 `json:"tcId"`
Passed *bool `json:"testPassed"`
}

type eddsaSigGenTestGroupResponse struct {
ID uint64 `json:"tgId"`
Q hexEncodedByteString `json:"q"`
Tests []eddsaSigGenTestCaseResponse `json:"tests"`
}

type eddsaSigGenTestCaseResponse struct {
ID uint64 `json:"tcId"`
Signature hexEncodedByteString `json:"signature"`
}

type eddsaSigVerTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []eddsaSigVerTestCaseResponse `json:"tests"`
}

type eddsaSigVerTestCaseResponse struct {
ID uint64 `json:"tcId"`
Passed *bool `json:"testPassed"`
}
1 change: 1 addition & 0 deletions util/fipstools/acvp/acvptool/subprocess/subprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess
"KAS-FFC-SSC": &kasDH{},
"PBKDF": &pbkdf{},
"ML-KEM": &mlKem{},
"EDDSA": &eddsa{},
}
m.primitives["ECDSA"] = &ecdsa{"ECDSA", map[string]bool{"P-224": true, "P-256": true, "P-384": true, "P-521": true}, m.primitives}

Expand Down
Binary file added util/fipstools/acvp/acvptool/test/expected/EDDSA.bz2
Binary file not shown.
5 changes: 4 additions & 1 deletion util/fipstools/acvp/acvptool/test/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@
{"Wrapper": "modulewrapper", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KDA-HKDF.bz2", "Out": "expected/KDA-HKDF.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KDA-OneStep.bz2", "Out": "expected/KDA-OneStep.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"}
{"Wrapper": "modulewrapper", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/EDDSA-KeyGen.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/EDDSA-SigGen.bz2"}
]
Binary file not shown.
Binary file not shown.
Binary file added util/fipstools/acvp/acvptool/test/vectors/EDDSA.bz2
Binary file not shown.
Loading

0 comments on commit 307898a

Please sign in to comment.