From d310345cbc8862fd13567a76db5482864a6ba287 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Tue, 14 Jan 2025 19:25:10 +0000 Subject: [PATCH] Wire-up ACVP --- util/fipstools/acvp/acvptool/acvp.go | 9 ++ .../acvp/acvptool/katemitter/emitter.go | 87 ++++++++++++++++++ .../acvp/acvptool/subprocess/eddsa.go | 59 ++++++++++-- .../acvp/acvptool/test/expected/EDDSA.bz2 | Bin 281 -> 286 bytes .../acvptool/test/vectors/EDDSA-KeyGen.bz2 | Bin 287 -> 286 bytes .../acvptool/test/vectors/EDDSA-SigGen.bz2 | Bin 513 -> 951 bytes .../acvp/acvptool/test/vectors/EDDSA.bz2 | Bin 687 -> 972 bytes .../acvp/modulewrapper/modulewrapper.cc | 49 +++++++++- 8 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 util/fipstools/acvp/acvptool/katemitter/emitter.go diff --git a/util/fipstools/acvp/acvptool/acvp.go b/util/fipstools/acvp/acvptool/acvp.go index 8cdb89de49..2d89d1dfb7 100644 --- a/util/fipstools/acvp/acvptool/acvp.go +++ b/util/fipstools/acvp/acvptool/acvp.go @@ -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" ) @@ -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 { @@ -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 { diff --git a/util/fipstools/acvp/acvptool/katemitter/emitter.go b/util/fipstools/acvp/acvptool/katemitter/emitter.go new file mode 100644 index 0000000000..cbca3e9761 --- /dev/null +++ b/util/fipstools/acvp/acvptool/katemitter/emitter.go @@ -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() +} diff --git a/util/fipstools/acvp/acvptool/subprocess/eddsa.go b/util/fipstools/acvp/acvptool/subprocess/eddsa.go index 2f8698b73f..c7ca48597c 100644 --- a/util/fipstools/acvp/acvptool/subprocess/eddsa.go +++ b/util/fipstools/acvp/acvptool/subprocess/eddsa.go @@ -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 @@ -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) @@ -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 } @@ -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"` } } @@ -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) +} diff --git a/util/fipstools/acvp/acvptool/test/expected/EDDSA.bz2 b/util/fipstools/acvp/acvptool/test/expected/EDDSA.bz2 index 954e023b5139856fe589b005522004f54e943beb..e9160d80cd39644838a881b1081f9bd863f6af82 100644 GIT binary patch literal 286 zcmV+(0pb2aT4*^jL0KkKSrrCWrvL!-|A0hPKmbq%{?I0%P8u)g-wH4R7y*C*qfG#0 z&>9SY&@ccrX`l?613{1)0x1YgrYEVP=%=QW$sSYDHjJm`O3`sGA_Clw6M{XMYG~2q z&7)D_<7%K95KV}a{2#A7FEeNPK6!!IVX5*putR{!Fbzauw@K-K>WHRc*v$+(Dmov~r9+E#Zt6{KT{pWL-Fa};!rmr$|- kFauiyc^lfAj`EeU1x${{JzSU}jt~4@$rRy2KvWr8oE{N^2mk;8 literal 281 zcmV+!0p|WfT4*^jL0KkKSxl;b)c^p@|A0hPKmbq%{_rNCP8u)g-wH4R8Bh`_Q`t<7 zPgB&%wHjuoMol!(Z71nN05s8{(+~gv41fxxnrSkb4^u-Sh|>tu5E(2H$#POI=JlMK zl_;%S*)?h}$8dyngT@5>n)6`2ZZ0l6k^2cL9n7gD$W;u6GxIiIsD|3nB1;9Jq`Lje z(-jd_%gR2j(^f^YJ&!R_C6Qep)ClGI4du_T8%mGUYKRV4+8u+9nzJB8wy fT0cb+CvilRWK<5M@G%(8%luu*6yZWaGO7Pm(!zVr diff --git a/util/fipstools/acvp/acvptool/test/vectors/EDDSA-KeyGen.bz2 b/util/fipstools/acvp/acvptool/test/vectors/EDDSA-KeyGen.bz2 index 66c8536078ad093b0004823096498997e52bf9e3..abd04b66c240643a4a6f7960dc72c36a4b059eb4 100644 GIT binary patch delta 271 zcmV+q0r38x0-gd6LRx4!F+o`-Q(32GU9yo58Gj0TLF#&$GBg3|22BIh9#CqYDYZRF z&;ZED&={D5L4=Y`H5zC%&Y|du@|gBCnBYU8pOc1SZ^MXik_>7>1a4%5 zdw4HUXrV;jX0Uhx69veyQh~xzg>YgNB0OS9$w_tIX7^NdOz@v#1siz2%Vd%9p`;9E zl2~H0bk~r0T!_hPqH>*TQ#_KIAw6uTcBq`aKY=8%NdQ<=a delta 272 zcmV+r0q_2v0-pj7LRx4!F+o`-Q(2_}p52iS8GmXGLF#Gg8X5rg0qO(P9#CqVB-7N5 zGBhy^OqeEvCYk{xlTAjN4Kx}68UQo^fRXkS9GO&n?Oo$rc@NJt_DRa4(RzH_`t#j zERs@VDO9=?cdArnJk_+DI+V>Mrg%?Wm7V(ah_1~M;Fj4rGE5C*pk(&aW`rTu#voaf WYX)7=W%)`W{9VZu;X*=`277e8t#+RP diff --git a/util/fipstools/acvp/acvptool/test/vectors/EDDSA-SigGen.bz2 b/util/fipstools/acvp/acvptool/test/vectors/EDDSA-SigGen.bz2 index 98199ace179320f98ac34ad7e4d585fce6762d5c..6ef758194e8d9efdc3686e279cd9124dd5671cb7 100644 GIT binary patch literal 951 zcmV;o14#TrT4*^jL0KkKS=kNFy#NDW-+)z6PzV3;KjaP?FX!KCPy_xJ;1mIH2&O5M z(?FOL0x~qfCIr(6BB}mNnlx&s`lxA>5CF-eCQO7!yEb83&|#4K9O@Vj&)w6ETMV#baK1>%6oy8Vzf%xp66lx;t0t z0x}?TK?)Z0?bY_4leFmSXTh`EiaW8Cc*(0Fn@IaLZ}K0x*NWqtP_r8)tGu;&)z7IoCz4 zsqzi30!n-IaV$K+6B=JaOM%fRU=7agdt2LYuD$(#j5PyTifxGTc(J=dlEGMJ<>8){TI@NnqS;)5l#^bV zWGIA8vu{8l%d$ppHDYwMC5;7D5fB5NIBFJ$BIx+gpi6RJOQq%osq=_^VfiaW3Vdck zf*kl1*q+d~j8zB6X%omv=NuH?$=YJI5Mh1*;E`E%H&X|8Su}zp0wR{6!N$$d*NPdd zc$r=x(1Pf+dbG(@ohEXmm`CF`U;%Ft>!l`aWn~^t6IEfWl^>pIpP9|kq^9Ri(@9~H zP0#cmTOIQ4I}YyuHx$n)Yj=wSCmcdoQ1?9OahUB@i8C`9>fmu-No~UDS7y0NIBkxz z7R)hkG-)*K%-lU}zvKNNk4ZUVwcqxefpT literal 513 zcmV+c0{;C%T4*^jL0KkKSwWe$cmM%&-+)z6PzV3;Kj01;FX!JXKmpwD=q_;7)jXP> zrJXs+MCfq zrcD4e1}CTusSrWFK+L6&i1egK0t5;|1Ql0F4PEpoW|$<*92sOXRHfOAIR5 zkHElt66er2K#C(UtT_V|ND@e+2rz?an3nxlK}pNYrfGF9xHpGv2suwqopj<>4F5ol zBoq*IlUaos;jr~ORdzF+LAdX&S5t)W2eLIxxDzuZQX*sbG}P$c=u&Paf-)ACAmBl8 zpx;0cR~LhV+yyQT69XiS?2k3Ih{9Mo8bzrJm1|ThF26Z%uJ+wqD zZgmw1TP(5#kEG#zJV$Ytq~|4DK6kcpV4+E)OVW5+vmd D(b(BD diff --git a/util/fipstools/acvp/acvptool/test/vectors/EDDSA.bz2 b/util/fipstools/acvp/acvptool/test/vectors/EDDSA.bz2 index 4d32631402857ba14e368e898d299dcf438fd61a..f3fa2c53e62b74790ca6f5285a390839ba789fdf 100644 GIT binary patch literal 972 zcmV;-12gD7-(*Ofz-+)z6PzV3;Kj04>sIp&_QjiE;Leeh+g@EZe?ybQb}@T*pn9>+9RvMnOANejeXZy*W8r-X=> zHe76mZiW`AR!%9%}L_i!n0nj5`(Cr0FW(tvgIYiRHj}C!ZCq&EuVtNcHV7Jhc-Yj)+y;)-F zTcpPws|TUwW*A!4fv6Lk(@kpm-RAb1&-6(T3anZ%8 zH=~YVk0iU`?zOEsDb7p@>sH1baMr+Z$Fy~%D-uxEr0SJ+Fq{#&Sn4vw`4fb+matfO{TVx+Ct(z41JZTq`SmOH3YhA*{!$ryIyFIf{s4 z69VGWQJ}Dte@kl#m>3Y~D^6yj!z;O=X@L&2D>E@sGI-)#&A%Zu>25R_6uh~psmuWY uMAKskR7Y#AqeCI3yv$i+m93nNg-pqR9IqD^2xTZg;_gVN3KARGiCSP1ys0$+ literal 687 zcmV;g0#N-zT4*^jL0KkKS?aS1rvL&t-+)z6PzV3;Kj041UI&uPJY9uhTj5{Y7Gn%%UEh>emsrdO|se_h#dlyJ#@RlvgoKK^d^NU2ES-#^#F> z$n8~uG=wsFs__-6MlEuzSer(>sAdNEw-_DONXbb#9iwFE*Q$WTg}EH(oAeQb9qUAg z44Ra*CM1D^;1mpDJn`DuLv%R{<4}}Eq|4ibF)Nu&Ib9M7cj37aO2i`&Q(g3CLyhi;As1Fuy&SV;xF9Up zswfiL)0$bbRduZ9s@6U?!^<>;(~U+qk`#%kggq{@dR7M9m@MH9M{Uy-EcxFh1limK zLiWN>c&sZ*FT-V%n%zOpSHPl-;g7=qqnzACV$||U5pqBI+^B2Rs&$!fFtNV#=Uj@W z1A1-~^!2ZSmuc#I7ZyrO^rFRy11T3~iFIvBf+Gybu%w`N!cIGV6iGm^v6d1@z@koH zS_z6Q7|35xsw4a9VJw=uNRd?`GED%^m{A*A$V^OLw5+Om%j!fNyhT8N Vg8JZGFvG9$cO+AV2@b0;nsDexG8q5> diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc index 5328922589..82c4ed3f9f 100644 --- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc +++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc @@ -1368,14 +1368,16 @@ static bool GetConfig(const Span 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( reinterpret_cast(kConfig), sizeof(kConfig) - 1)}); @@ -3072,6 +3074,45 @@ static bool ED25519SigVer(const Span args[], return write_reply({Span(reply)}); } +static bool ED25519phSigGen(const Span args[], + ReplyCallback write_reply) { + const Span seed = args[0]; + const Span message = args[1]; + const Span context = args[2]; + + std::vector private_key(ED25519_PRIVATE_KEY_LEN); + std::vector public_key(ED25519_PUBLIC_KEY_LEN); + std::vector 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(signature)}); +} + +static bool ED25519phSigVer(const Span args[], + ReplyCallback write_reply) { + const Span message = args[0]; + const Span public_key = args[1]; + const Span signature = args[2]; + const Span 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(reply)}); +} + static struct { char name[kMaxNameLength + 1]; uint8_t num_expected_args; @@ -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> args) { const bssl::Span algorithm = args[0];