| package subprocess |
| |
| import ( |
| "encoding/hex" |
| "encoding/json" |
| "fmt" |
| "strings" |
| ) |
| |
| // Common top-level structure to parse mode |
| type mlkemTestVectorSet struct { |
| Algorithm string `json:"algorithm"` |
| Mode string `json:"mode"` |
| Revision string `json:"revision"` |
| } |
| |
| // Key generation specific structures |
| type mlkemKeyGenTestVectorSet struct { |
| Algorithm string `json:"algorithm"` |
| Mode string `json:"mode"` |
| Revision string `json:"revision"` |
| Groups []mlkemKeyGenTestGroup `json:"testGroups"` |
| } |
| |
| type mlkemKeyGenTestGroup struct { |
| ID uint64 `json:"tgId"` |
| TestType string `json:"testType"` |
| ParameterSet string `json:"parameterSet"` |
| Tests []mlkemKeyGenTest `json:"tests"` |
| } |
| |
| type mlkemKeyGenTest struct { |
| ID uint64 `json:"tcId"` |
| Z string `json:"z"` |
| D string `json:"d"` |
| } |
| |
| type mlkemKeyGenTestGroupResponse struct { |
| ID uint64 `json:"tgId"` |
| Tests []mlkemKeyGenTestResponse `json:"tests"` |
| } |
| |
| type mlkemKeyGenTestResponse struct { |
| ID uint64 `json:"tcId"` |
| EK string `json:"ek"` |
| DK string `json:"dk"` |
| } |
| |
| type mlkemEncapDecapTestVectorSet struct { |
| Algorithm string `json:"algorithm"` |
| Mode string `json:"mode"` |
| Revision string `json:"revision"` |
| Groups []mlkemEncapDecapTestGroup `json:"testGroups"` |
| } |
| |
| type mlkemEncapDecapTestGroup struct { |
| ID uint64 `json:"tgId"` |
| TestType string `json:"testType"` |
| ParameterSet string `json:"parameterSet"` |
| Function string `json:"function"` |
| DK string `json:"dk,omitempty"` |
| Tests []mlkemEncapDecapTest `json:"tests"` |
| } |
| |
| type mlkemEncapDecapTest struct { |
| ID uint64 `json:"tcId"` |
| EK string `json:"ek,omitempty"` |
| M string `json:"m,omitempty"` |
| C string `json:"c,omitempty"` |
| } |
| |
| type mlkemEncapDecapTestGroupResponse struct { |
| ID uint64 `json:"tgId"` |
| Tests []mlkemEncapDecapTestResponse `json:"tests"` |
| } |
| |
| type mlkemEncapDecapTestResponse struct { |
| ID uint64 `json:"tcId"` |
| C string `json:"c,omitempty"` |
| K string `json:"k,omitempty"` |
| } |
| |
| type mlkem struct{} |
| |
| func (m *mlkem) Process(vectorSet []byte, t Transactable) (any, error) { |
| var common mlkemTestVectorSet |
| if err := json.Unmarshal(vectorSet, &common); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal vector set: %v", err) |
| } |
| |
| switch common.Mode { |
| case "keyGen": |
| return m.processKeyGen(vectorSet, t) |
| case "encapDecap": |
| return m.processEncapDecap(vectorSet, t) |
| default: |
| return nil, fmt.Errorf("unsupported ML-KEM mode: %q", common.Mode) |
| } |
| } |
| |
| func (m *mlkem) processKeyGen(vectorSet []byte, t Transactable) (any, error) { |
| var parsed mlkemKeyGenTestVectorSet |
| if err := json.Unmarshal(vectorSet, &parsed); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal keyGen vector set: %v", err) |
| } |
| |
| var ret []mlkemKeyGenTestGroupResponse |
| |
| for _, group := range parsed.Groups { |
| response := mlkemKeyGenTestGroupResponse{ |
| ID: group.ID, |
| } |
| |
| if !strings.HasPrefix(group.ParameterSet, "ML-KEM-") { |
| return nil, fmt.Errorf("invalid parameter set: %s", group.ParameterSet) |
| } |
| cmdName := group.ParameterSet + "/keyGen" |
| |
| for _, test := range group.Tests { |
| // Concatenate d and z to form the seed |
| dBytes, err := hex.DecodeString(test.D) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decode d in test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| zBytes, err := hex.DecodeString(test.Z) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decode z in test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| seed := make([]byte, len(dBytes)+len(zBytes)) |
| copy(seed, dBytes) |
| copy(seed[len(dBytes):], zBytes) |
| |
| result, err := t.Transact(cmdName, 2, seed) |
| if err != nil { |
| return nil, fmt.Errorf("key generation failed for test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| response.Tests = append(response.Tests, mlkemKeyGenTestResponse{ |
| ID: test.ID, |
| EK: hex.EncodeToString(result[0]), |
| DK: hex.EncodeToString(result[1]), |
| }) |
| } |
| |
| ret = append(ret, response) |
| } |
| |
| return ret, nil |
| } |
| |
| func (m *mlkem) processEncapDecap(vectorSet []byte, t Transactable) (any, error) { |
| var parsed mlkemEncapDecapTestVectorSet |
| if err := json.Unmarshal(vectorSet, &parsed); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal encapDecap vector set: %v", err) |
| } |
| |
| var ret []mlkemEncapDecapTestGroupResponse |
| |
| for _, group := range parsed.Groups { |
| response := mlkemEncapDecapTestGroupResponse{ |
| ID: group.ID, |
| } |
| |
| if !strings.HasPrefix(group.ParameterSet, "ML-KEM-") { |
| return nil, fmt.Errorf("invalid parameter set: %s", group.ParameterSet) |
| } |
| |
| switch group.Function { |
| case "encapsulation": |
| cmdName := group.ParameterSet + "/encap" |
| for _, test := range group.Tests { |
| ek, err := hex.DecodeString(test.EK) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decode ek in test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| m, err := hex.DecodeString(test.M) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decode m in test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| result, err := t.Transact(cmdName, 2, ek, m) |
| if err != nil { |
| return nil, fmt.Errorf("encapsulation failed for test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| response.Tests = append(response.Tests, mlkemEncapDecapTestResponse{ |
| ID: test.ID, |
| C: hex.EncodeToString(result[0]), |
| K: hex.EncodeToString(result[1]), |
| }) |
| } |
| |
| case "decapsulation": |
| cmdName := group.ParameterSet + "/decap" |
| dk, err := hex.DecodeString(group.DK) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decode dk in group %d: %s", |
| group.ID, err) |
| } |
| |
| for _, test := range group.Tests { |
| c, err := hex.DecodeString(test.C) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decode c in test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| result, err := t.Transact(cmdName, 1, dk, c) |
| if err != nil { |
| return nil, fmt.Errorf("decapsulation failed for test case %d/%d: %s", |
| group.ID, test.ID, err) |
| } |
| |
| response.Tests = append(response.Tests, mlkemEncapDecapTestResponse{ |
| ID: test.ID, |
| K: hex.EncodeToString(result[0]), |
| }) |
| } |
| |
| default: |
| return nil, fmt.Errorf("unsupported function: %s", group.Function) |
| } |
| |
| ret = append(ret, response) |
| } |
| |
| return ret, nil |
| } |