Skip to content
Merged
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
118 changes: 118 additions & 0 deletions util/fipstools/acvp/acvptool/subprocess/kts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

package subprocess

import (
"encoding/json"
"fmt"
)

type ktsVectorSet struct {
Groups []ktsTestGroup `json:"testGroups"`
}

type ktsTestGroup struct {
ID uint64 `json:"tgId"`
Type string `json:"testType"`
Role string `json:"kasRole"`
Scheme string `json:"scheme"`
KtsConfiguration ktsConfiguration `json:"ktsConfiguration"`
Tests []ktsTest `json:"tests"`
L uint32 `json:"l"`
}

type ktsConfiguration struct {
HashAlg string `json:"hashAlg"`
}

type ktsTest struct {
ID uint64 `json:"tcId"`
ServerN hexEncodedByteString `json:"serverN"`
ServerE hexEncodedByteString `json:"serverE"`
IutN hexEncodedByteString `json:"iutN"`
IutE hexEncodedByteString `json:"iutE"`
IutP hexEncodedByteString `json:"iutP"`
IutQ hexEncodedByteString `json:"iutQ"`
IutD hexEncodedByteString `json:"iutD"`
Ct hexEncodedByteString `json:"serverC"`
}

type ktsTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []ktsTestResponse `json:"tests"`
}

type ktsTestResponse struct {
ID uint64 `json:"tcId"`
IutC hexEncodedByteString `json:"iutC,omitempty"`
Dkm hexEncodedByteString `json:"dkm"`
}

type kts struct{}

func (k *kts) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed ktsVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
}

// See https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ifc.html
var ret []ktsTestGroupResponse
for _, group := range parsed.Groups {
response := ktsTestGroupResponse{
ID: group.ID,
}

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

switch group.Role {
case "initiator", "responder":
default:
return nil, fmt.Errorf("unknown role %q", group.Role)
}

if group.Scheme != "KTS-OAEP-basic" {
return nil, fmt.Errorf("unknown scheme %q", group.Scheme)
}

if len(group.KtsConfiguration.HashAlg) == 0 {
return nil, fmt.Errorf("missing hash algorithm")
}

hashAlg := group.KtsConfiguration.HashAlg

var outLenBytes [4]byte
NativeEndian.PutUint32(outLenBytes[:], group.L/8) // Convert bits to bytes

for _, test := range group.Tests {
if group.Role == "initiator" {
result, err := m.Transact("KTS/OAEP/"+hashAlg+"/initiate", 2, outLenBytes[:], test.ServerN, test.ServerE)
if err != nil {
return nil, err
}
response.Tests = append(response.Tests, ktsTestResponse{
ID: test.ID,
IutC: result[0],
Dkm: result[1],
})
} else {
result, err := m.Transact("KTS/OAEP/"+hashAlg+"/respond", 1, test.Ct, test.IutN, test.IutE, test.IutQ, test.IutP, test.IutD)
if err != nil {
return nil, err
}

response.Tests = append(response.Tests, ktsTestResponse{
ID: test.ID,
Dkm: result[0],
})
}
}

ret = append(ret, response)
}

return ret, nil
}
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 @@ -162,6 +162,7 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess
"OneStep": &kdaOneStepMode{},
},
},
"KTS-IFC": &kts{},
"TLS-v1.3": &tls13{},
"CMAC-AES": &keyedMACPrimitive{"CMAC-AES"},
"RSA": &rsa{},
Expand Down
1 change: 1 addition & 0 deletions util/fipstools/acvp/acvptool/test/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
{"Wrapper": "modulewrapper", "In": "vectors/KAS-ECC-SSC.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KAS-FFC-SSC.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KDF.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KTS-IFC.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-GCM-internal-IV.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/RSA.bz2", "Out": "expected/RSA.bz2"},
Expand Down
Binary file not shown.
129 changes: 129 additions & 0 deletions util/fipstools/acvp/modulewrapper/modulewrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,25 @@ static bool GetConfig(const Span<const uint8_t> args[],
"internal",
"external"
]
},)"
R"({
"algorithm": "KTS-IFC",
"revision": "Sp800-56Br2",
"iutId": "ABCD",
"function": ["keyPairGen", "partialVal"],
"keyGenerationMethods": ["rsakpg1-basic"],
"modulo": [2048, 3072, 4096],
"fixedPubExp": "010001",
"scheme": {
"KTS-OAEP-basic": {
"kasRole": ["initiator", "responder"],
"ktsMethod": {
"hashAlgs": ["SHA-1", "SHA2-224", "SHA2-256", "SHA2-384", "SHA2-512"],
"supportsNullAssociatedData": true
},
"l": 1024
}
}
}])";
return write_reply({Span<const uint8_t>(
reinterpret_cast<const uint8_t *>(kConfig), sizeof(kConfig) - 1)});
Expand Down Expand Up @@ -2779,6 +2798,106 @@ static bool RSASigVer(const Span<const uint8_t> args[],
return write_reply({Span<const uint8_t>(&ok, 1)});
}

template <const EVP_MD *(MDFunc)()>
static bool RSAOAEPEncrypt(const Span<const uint8_t> args[],
ReplyCallback write_reply) {
const Span<const uint8_t> out_len_bytes = args[0];
const Span<const uint8_t> n_bytes = args[1];
const Span<const uint8_t> e_bytes = args[2];

uint32_t out_len = 0;
memcpy(&out_len, out_len_bytes.data(), sizeof(out_len));

BIGNUM *n = BN_new();
BIGNUM *e = BN_new();
bssl::UniquePtr<RSA> rsa(RSA_new());

if (!BN_bin2bn(n_bytes.data(), n_bytes.size(), n) ||
!BN_bin2bn(e_bytes.data(), e_bytes.size(), e) ||
!RSA_set0_key(rsa.get(), n, e, nullptr)) {
return false;
}

bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) {
return false;
}

bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
if (!ctx || !EVP_PKEY_encrypt_init(ctx.get()) ||
!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) ||
!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), MDFunc())) {
return false;
}

// Randomly generate the keying material to encrypt
std::vector<uint8_t> out(out_len);
RAND_bytes(out.data(), out.size());

size_t ct_len = 0;
if (!EVP_PKEY_encrypt(ctx.get(), nullptr, &ct_len, out.data(), out.size())) {
return false;
}
std::vector<uint8_t> ct(ct_len);
if (!EVP_PKEY_encrypt(ctx.get(), ct.data(), &ct_len, out.data(),
out.size())) {
return false;
}
return write_reply({Span<const uint8_t>(ct), Span<const uint8_t>(out)});
}

template <const EVP_MD *(MDFunc)()>
static bool RSAOAEPDecrypt(const Span<const uint8_t> args[],
ReplyCallback write_reply) {
const Span<const uint8_t> input = args[0];
const Span<const uint8_t> n_bytes = args[1];
const Span<const uint8_t> e_bytes = args[2];
const Span<const uint8_t> q_bytes = args[3];
const Span<const uint8_t> p_bytes = args[4];
const Span<const uint8_t> d_bytes = args[5];

BIGNUM *n = BN_new();
BIGNUM *e = BN_new();
BIGNUM *p = BN_new();
BIGNUM *q = BN_new();
BIGNUM *d = BN_new();
bssl::UniquePtr<RSA> rsa(RSA_new());

if (!BN_bin2bn(n_bytes.data(), n_bytes.size(), n) ||
!BN_bin2bn(e_bytes.data(), e_bytes.size(), e) ||
!BN_bin2bn(d_bytes.data(), d_bytes.size(), d) ||
!BN_bin2bn(p_bytes.data(), p_bytes.size(), p) ||
!BN_bin2bn(q_bytes.data(), q_bytes.size(), q) ||
!RSA_set0_key(rsa.get(), n, e, d) || !RSA_set0_factors(rsa.get(), p, q)) {
return false;
}

bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) {
return false;
}

bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
if (!ctx || !EVP_PKEY_decrypt_init(ctx.get()) ||
!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) ||
!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), MDFunc())) {
return false;
}

size_t out_len = 0;
if (!EVP_PKEY_decrypt(ctx.get(), nullptr, &out_len, input.data(),
input.size())) {
return false;
}
std::vector<uint8_t> out(out_len);
if (!EVP_PKEY_decrypt(ctx.get(), out.data(), &out_len, input.data(),
input.size())) {
return false;
}
out.resize(out_len);
return write_reply({Span<const uint8_t>(out)});
}

template <const EVP_MD *(MDFunc)()>
static bool TLSKDF(const Span<const uint8_t> args[],
ReplyCallback write_reply) {
Expand Down Expand Up @@ -3674,6 +3793,16 @@ static struct {
{"KDA/OneStep/HMAC-SHA2-512", 4, SSKDF_HMAC<EVP_sha512>},
{"KDA/OneStep/HMAC-SHA2-512/224", 4, SSKDF_HMAC<EVP_sha512_224>},
{"KDA/OneStep/HMAC-SHA2-512/256", 4, SSKDF_HMAC<EVP_sha512_256>},
{"KTS/OAEP/SHA-1/initiate", 3, RSAOAEPEncrypt<EVP_sha1>},
{"KTS/OAEP/SHA2-224/initiate", 3, RSAOAEPEncrypt<EVP_sha224>},
{"KTS/OAEP/SHA2-256/initiate", 3, RSAOAEPEncrypt<EVP_sha256>},
{"KTS/OAEP/SHA2-384/initiate", 3, RSAOAEPEncrypt<EVP_sha384>},
{"KTS/OAEP/SHA2-512/initiate", 3, RSAOAEPEncrypt<EVP_sha512>},
{"KTS/OAEP/SHA-1/respond", 6, RSAOAEPDecrypt<EVP_sha1>},
{"KTS/OAEP/SHA2-224/respond", 6, RSAOAEPDecrypt<EVP_sha224>},
{"KTS/OAEP/SHA2-256/respond", 6, RSAOAEPDecrypt<EVP_sha256>},
{"KTS/OAEP/SHA2-384/respond", 6, RSAOAEPDecrypt<EVP_sha384>},
{"KTS/OAEP/SHA2-512/respond", 6, RSAOAEPDecrypt<EVP_sha512>},
{"SSHKDF/SHA-1/ivCli", 4,
SSHKDF<EVP_sha1, EVP_KDF_SSHKDF_TYPE_INITIAL_IV_CLI_TO_SRV>},
{"SSHKDF/SHA2-224/ivCli", 4,
Expand Down
Loading