Skip to content

Commit

Permalink
Wire-up ACVP
Browse files Browse the repository at this point in the history
  • Loading branch information
skmcgrail committed Jan 17, 2025
1 parent 0873ba1 commit d310345
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 9 deletions.
9 changes: 9 additions & 0 deletions util/fipstools/acvp/acvptool/acvp.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"time"

"boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/acvp"
"boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/katemitter"
"boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/subprocess"
)

Expand All @@ -51,6 +52,7 @@ var (
expectedOutFlag = flag.String("expected-out", "", "Name of a file to write the expected results to")
wrapperPath = flag.String("wrapper", "../../../../build/util/fipstools/acvp/modulewrapper/modulewrapper", "Path to the wrapper binary")
waitForDebugger = flag.Bool("wait-for-debugger", false, "If true, jobs will run one at a time and pause for a debugger to attach")
katFilePath = flag.String("kat-out", "", "Writes a KAT file out if with test information for use with AWS-LC's file-based test framework. Support is limited, so if you don't see content its likely not plumbed in.")
)

type Config struct {
Expand Down Expand Up @@ -555,6 +557,13 @@ func main() {
log.Fatalf("failed to parse configuration from Middle: %s", err)
}

if len(*katFilePath) > 0 {
if err := katemitter.EmitToFile(*katFilePath); err != nil {
log.Fatalf("failed to start kat emitter: %v", err)
}
defer katemitter.Close()
}

if *dumpRegcap {
nonTestAlgos := make([]map[string]interface{}, 0, len(supportedAlgos))
for _, algo := range supportedAlgos {
Expand Down
87 changes: 87 additions & 0 deletions util/fipstools/acvp/acvptool/katemitter/emitter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// katemitter is a package which (with best effort) will attempt to emit test case data to a configured file
// for use by AWS-LC's file-based test framework. "Attempt" because it doesn't check for errors on the writer
// destination. But this is mostly a facility to aid in getting KATs for algorithms and re-using the inputs/outputs
// from the ACVP server after they have been validated as accurate.
package katemitter

import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
)

type stubEmitter struct{}

func (s *stubEmitter) Close() error {
return nil
}

func (s *stubEmitter) Write(p []byte) (n int, err error) {
return len(p), nil
}

var katDestination io.WriteCloser = &stubEmitter{}

func EmitToFile(path string) error {
if err := Close(); err != nil {
return err
}

f, err := os.Create(path)
if err != nil {
return err
}

katDestination = f

return nil
}

func NewTestCase(comment string) {
var buffer bytes.Buffer
buffer.WriteRune('\n')
buffer.WriteString("# Case: ")
buffer.WriteString(comment)
buffer.WriteRune('\n')
_, _ = io.Copy(katDestination, &buffer)
}

func NewSection(title string) {
var buffer bytes.Buffer
buffer.WriteRune('\n')
buffer.WriteString("# Section: ")
buffer.WriteString(title)
buffer.WriteByte('\n')
_, _ = io.Copy(katDestination, &buffer)
}

func WriteComment(comment string) {
_, _ = katDestination.Write([]byte("# " + comment + "\n"))
}

func WriteStringKvPair(key string, value string) {
_, _ = katDestination.Write([]byte(fmt.Sprintf("%s = %s\n", key, value)))
}

func WriteBytesKvPair(key string, value []byte) {
_, _ = katDestination.Write([]byte(fmt.Sprintf("%s = %s\n", key, hex.EncodeToString(value))))
}

func WriteIntKvPair(key string, value int) {
_, _ = katDestination.Write([]byte(fmt.Sprintf("%s = %d\n", key, value)))
}

func WriteInt64KvPair(key string, value int64) {
_, _ = katDestination.Write([]byte(fmt.Sprintf("%s = %d\n", key, value)))
}

func WriteUInt64KvPair(key string, value uint64) {
_, _ = katDestination.Write([]byte(fmt.Sprintf("%s = %d\n", key, value)))
}

func Close() error {
_, _ = katDestination.Write([]byte("\n"))
return katDestination.Close()
}
59 changes: 53 additions & 6 deletions util/fipstools/acvp/acvptool/subprocess/eddsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"encoding/json"
"fmt"
"strings"

"boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/katemitter"
)

// NIST ACVP EDDSA Schema: https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html
Expand Down Expand Up @@ -130,28 +132,50 @@ func processEddsaSigGenTestGroup(testGroups json.RawMessage, m Transactable) (in
return nil, fmt.Errorf("unsupported test type %q", group.Type)
}

if group.Prehash {
katemitter.NewSection(fmt.Sprintf("HashEdDSA %s %s", group.Curve, group.Type))
} else {
katemitter.NewSection(fmt.Sprintf("EdDSA %s %s", group.Curve, group.Type))
}

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

seed := results[0]
q := results[1]

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

for _, test := range group.Tests {
results, err := m.Transact("EDDSA/"+string(group.Curve)+"/sigGen", 1, seed, test.Message)
command := "EDDSA/" + string(group.Curve) + "/sigGen"
args := [][]byte{seed, test.Message}

if group.Prehash {
if test.ContextLength != len(test.Context) {
return nil, fmt.Errorf("mismatch between context and contextLength, %v != %v", test.ContextLength, len(test.Context))
}
command += "/preHash"
args = append(args, test.Context)
}

results, err := m.Transact(command, 1, args...)
if err != nil {
return nil, err
}

signature := results[0]

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

emitSigGenKatTestCase(test.ID, seed, q, test.Message, test.Context, signature)
}

ret = append(ret, response)
Expand All @@ -178,7 +202,17 @@ func processEddsaSigVerTestGroup(testGroups json.RawMessage, m Transactable) (in
}

for _, test := range group.Tests {
results, err := m.Transact("EDDSA/"+string(group.Curve)+"/sigVer", 1, test.Message, test.Q, test.Signature)
command := "EDDSA/" + string(group.Curve) + "/sigVer"
args := [][]byte{test.Message, test.Q, test.Signature}

if group.Prehash {
command += "/preHash"
// ACVP sigVer supports HashEdDSA/PreHash but doesn't list context as given in the schema?
// Assuming an empty context here for now until the schema changes...
args = append(args, []byte{})
}

results, err := m.Transact(command, 1, args...)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -247,8 +281,10 @@ type eddsaSigGenTestGroup struct {
Prehash bool `json:"prehash"`
Type string `json:"testType"`
Tests []struct {
ID uint64 `json:"tcId"`
Message hexEncodedByteString `json:"message"`
ID uint64 `json:"tcId"`
Message hexEncodedByteString `json:"message"`
Context hexEncodedByteString `json:"context"`
ContextLength int `json:"contextLength"`
}
}

Expand Down Expand Up @@ -306,3 +342,14 @@ type eddsaSigVerTestCaseResponse struct {
ID uint64 `json:"tcId"`
Passed *bool `json:"testPassed"`
}

func emitSigGenKatTestCase(id uint64, seed, q, message, context, signature []byte) {
katemitter.NewTestCase(fmt.Sprintf("%d", id))
katemitter.WriteBytesKvPair("SEED", seed)
katemitter.WriteBytesKvPair("Q", q)
katemitter.WriteBytesKvPair("MESSAGE", message)
if len(context) > 0 {
katemitter.WriteBytesKvPair("CONTEXT", context)
}
katemitter.WriteBytesKvPair("SIGNATURE", signature)
}
Binary file modified util/fipstools/acvp/acvptool/test/expected/EDDSA.bz2
Binary file not shown.
Binary file modified util/fipstools/acvp/acvptool/test/vectors/EDDSA-KeyGen.bz2
Binary file not shown.
Binary file modified util/fipstools/acvp/acvptool/test/vectors/EDDSA-SigGen.bz2
Binary file not shown.
Binary file modified util/fipstools/acvp/acvptool/test/vectors/EDDSA.bz2
Binary file not shown.
49 changes: 46 additions & 3 deletions util/fipstools/acvp/modulewrapper/modulewrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1368,14 +1368,16 @@ static bool GetConfig(const Span<const uint8_t> args[],
"revision": "1.0",
"curve": ["ED-25519"],
"pure": true,
"preHash": false
"preHash": true,
"contextLength": [{"min": 0, "max": 255, "increment": 1}]
},{
"algorithm": "EDDSA",
"mode": "sigVer",
"revision": "1.0",
"curve": ["ED-25519"],
"pure": true,
"preHash": false
"preHash": true,
"contextLength": [{"min": 0, "max": 255, "increment": 1}]
}])";
return write_reply({Span<const uint8_t>(
reinterpret_cast<const uint8_t *>(kConfig), sizeof(kConfig) - 1)});
Expand Down Expand Up @@ -3072,6 +3074,45 @@ static bool ED25519SigVer(const Span<const uint8_t> args[],
return write_reply({Span<const uint8_t>(reply)});
}

static bool ED25519phSigGen(const Span<const uint8_t> args[],
ReplyCallback write_reply) {
const Span<const uint8_t> seed = args[0];
const Span<const uint8_t> message = args[1];
const Span<const uint8_t> context = args[2];

std::vector<uint8_t> private_key(ED25519_PRIVATE_KEY_LEN);
std::vector<uint8_t> public_key(ED25519_PUBLIC_KEY_LEN);
std::vector<uint8_t> signature(ED25519_SIGNATURE_LEN);

::ED25519_keypair_from_seed(public_key.data(), private_key.data(),
seed.data());

if (!::ED25519ph_sign(signature.data(), message.data(), message.size(),
private_key.data(), context.data(), context.size())) {
return false;
}

return write_reply({Span<const uint8_t>(signature)});
}

static bool ED25519phSigVer(const Span<const uint8_t> args[],
ReplyCallback write_reply) {
const Span<const uint8_t> message = args[0];
const Span<const uint8_t> public_key = args[1];
const Span<const uint8_t> signature = args[2];
const Span<const uint8_t> context = args[3];

uint8_t reply[1] = {0};
if (::ED25519ph_verify(message.data(), message.size(), signature.data(),
public_key.data(), context.data(), context.size())) {
reply[0] = 1;
} else {
ERR_clear_error();
}

return write_reply({Span<const uint8_t>(reply)});
}

static struct {
char name[kMaxNameLength + 1];
uint8_t num_expected_args;
Expand Down Expand Up @@ -3320,7 +3361,9 @@ static struct {
{"EDDSA/ED-25519/keyGen", 0, ED25519KeyGen},
{"EDDSA/ED-25519/keyVer", 1, ED25519KeyVer},
{"EDDSA/ED-25519/sigGen", 2, ED25519SigGen},
{"EDDSA/ED-25519/sigVer", 3, ED25519SigVer}};
{"EDDSA/ED-25519/sigGen/preHash", 3, ED25519phSigGen},
{"EDDSA/ED-25519/sigVer", 3, ED25519SigVer},
{"EDDSA/ED-25519/sigVer/preHash", 4, ED25519phSigVer}};

Handler FindHandler(Span<const Span<const uint8_t>> args) {
const bssl::Span<const uint8_t> algorithm = args[0];
Expand Down

0 comments on commit d310345

Please sign in to comment.