From a478c7d14ea2237c5bb3239b67cecf944b4078ed Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 18 Nov 2025 12:35:40 -0500 Subject: [PATCH 01/19] added go dependencies --- post-quantum-server-testing/src/go.mod | 40 +++++++++++++++ post-quantum-server-testing/src/go.sum | 71 ++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 post-quantum-server-testing/src/go.mod create mode 100644 post-quantum-server-testing/src/go.sum diff --git a/post-quantum-server-testing/src/go.mod b/post-quantum-server-testing/src/go.mod new file mode 100644 index 0000000..f97c356 --- /dev/null +++ b/post-quantum-server-testing/src/go.mod @@ -0,0 +1,40 @@ +module server-testing + +go 1.24.0 + +require ( + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.4 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/kms v1.23.2 // indirect + cloud.google.com/go/longrunning v0.6.7 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/open-quantum-safe/liboqs-go v0.0.0-20250119172907-28b5301df438 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/api v0.247.0 // indirect + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect + google.golang.org/grpc v1.74.2 // indirect + google.golang.org/protobuf v1.36.10 // indirect +) diff --git a/post-quantum-server-testing/src/go.sum b/post-quantum-server-testing/src/go.sum new file mode 100644 index 0000000..aaafab0 --- /dev/null +++ b/post-quantum-server-testing/src/go.sum @@ -0,0 +1,71 @@ +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.16.4 h1:fXOAIQmkApVvcIn7Pc2+5J8QTMVbUGLscnSVNl11su8= +cloud.google.com/go/auth v0.16.4/go.mod h1:j10ncYwjX/g3cdX7GpEzsdM+d+ZNsXAbb6qXA7p1Y5M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= +cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/kms v1.23.2 h1:4IYDQL5hG4L+HzJBhzejUySoUOheh3Lk5YT4PCyyW6k= +cloud.google.com/go/kms v1.23.2/go.mod h1:rZ5kK0I7Kn9W4erhYVoIRPtpizjunlrfU4fUkumUp8g= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/open-quantum-safe/liboqs-go v0.0.0-20250119172907-28b5301df438 h1:rqhyfDxqF50veu/A7HsgRBShVN8Gqz4mmrgtRr6KnLo= +github.com/open-quantum-safe/liboqs-go v0.0.0-20250119172907-28b5301df438/go.mod h1:OoIQ+v4rM6S6cF9zLGxsnsXX9vwv7WLp9s0TV2FbD6M= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc= +google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= +google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= From 96b9b183f24a7fee249a1c06aefb9bb7ff5ba770 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 18 Nov 2025 12:40:47 -0500 Subject: [PATCH 02/19] added signature generation code for MLDSA, Falcon, ECDSA, RSA --- .../src/ecdsa/ecdsa.go | 211 ++++++++++++++++++ .../src/falcon/falcon.go | 196 ++++++++++++++++ .../src/mldsa/mldsa.go | 195 ++++++++++++++++ post-quantum-server-testing/src/rsa/rsa.go | 211 ++++++++++++++++++ 4 files changed, 813 insertions(+) create mode 100644 post-quantum-server-testing/src/ecdsa/ecdsa.go create mode 100644 post-quantum-server-testing/src/falcon/falcon.go create mode 100644 post-quantum-server-testing/src/mldsa/mldsa.go create mode 100644 post-quantum-server-testing/src/rsa/rsa.go diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/ecdsa/ecdsa.go new file mode 100644 index 0000000..66af0d8 --- /dev/null +++ b/post-quantum-server-testing/src/ecdsa/ecdsa.go @@ -0,0 +1,211 @@ +package ecdsa + +import ( + "context" + "crypto/sha512" + "encoding/base64" + "fmt" + "hash/crc32" + "log" + "os" + "time" + + kms "cloud.google.com/go/kms/apiv1" + kmspb "cloud.google.com/go/kms/apiv1/kmspb" + "github.com/joho/godotenv" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// This is a struct for the request message +type request struct { + Input string `json:"input"` + KeyID string `json:"keyid"` + Auth string `json:"auth"` +} + +// This is a struct for the output signature +type response struct { + Signature string `json:"signature"` + PublicKey string `json:"publicKey"` +} + +// use godot package to load/read the .env file and +// return the value of the key +func GoDotEnvVariable(key string) string { + // Load .env file + err := godotenv.Load("../.env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + return os.Getenv(key) +} + +// This function processes the request struct and returns a struct of the data +func parseInput(req request) ([]byte, string, error) { + if req.Input == "" { + return nil, "", fmt.Errorf("missing the json data input") + } + if req.KeyID == "" { + return nil, "", fmt.Errorf("missing the json keyid") + } + if req.Auth == "" { + return nil, "", fmt.Errorf("missing the json auth") + } + + dcd, err := base64.StdEncoding.DecodeString(req.Input) + if err != nil { + return nil, "", fmt.Errorf("error decoding base64 input %v", err) + } + + // This returns the json []byte + return dcd, req.KeyID, nil +} + +// getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS +func getPublicKey(name string) ([]byte, error) { + // Create the client. + ctx := context.Background() + client, err := kms.NewKeyManagementClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create kms client: %w", err) + } + defer client.Close() + + // Build the request. + req := &kmspb.GetPublicKeyRequest{ + Name: name, + } + + // Call the API. + result, err := client.GetPublicKey(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get public key: %w", err) + } + + // The 'Pem' field is the raw string representation of the public key. + // Convert 'Pem' into bytes for further processing. + key := []byte(result.Pem) + + // Perform integrity verification on result. + crc32c := func(data []byte) uint32 { + t := crc32.MakeTable(crc32.Castagnoli) + return crc32.Checksum(data, t) + } + if int64(crc32c(key)) != result.PemCrc32C.Value { + return nil, fmt.Errorf("getPublicKey: response corrupted in-transit") + } + + return key, nil +} + +// signAsymmetric will sign a plaintext message using a saved asymmetric private +// key stored in Cloud KMS. +func signAsymmetric(name string, message []byte) ([]byte, error) { + // Create the client. + ctx := context.Background() + client, err := kms.NewKeyManagementClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create kms client: %w", err) + } + defer client.Close() + + // ciphertexts are always byte arrays. + plaintext := message + + // Calculate the digest of the message. + digest := sha512.New384() + if _, err := digest.Write(plaintext); err != nil { + return nil, fmt.Errorf("failed to create digest: %w", err) + } + + // Compute digest's CRC32C. + crc32c := func(data []byte) uint32 { + t := crc32.MakeTable(crc32.Castagnoli) + return crc32.Checksum(data, t) + + } + digestCRC32C := crc32c(digest.Sum(nil)) + + // Build the signing request. + req := &kmspb.AsymmetricSignRequest{ + Name: name, + Digest: &kmspb.Digest{ + Digest: &kmspb.Digest_Sha384{ + Sha384: digest.Sum(nil), + }, + }, + DigestCrc32C: wrapperspb.Int64(int64(digestCRC32C)), + } + + // Call the API. + result, err := client.AsymmetricSign(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to sign digest: %w", err) + } + + // Perform integrity verification on result. + if !result.VerifiedDigestCrc32C { + return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") + } + if result.Name != req.Name { + return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") + } + if int64(crc32c(result.Signature)) != result.SignatureCrc32C.Value { + return nil, fmt.Errorf("AsymmetricSign: response corrupted in-transit") + } + + return result.Signature, nil +} + +func signData(input string, key string, auth string) response { + var reqData request + + location := GoDotEnvVariable("LOCATION") + keyID := GoDotEnvVariable("KEY_VERSION") + keyRing := GoDotEnvVariable("KEYRING") + projectID := GoDotEnvVariable("PROJECT_ID") + + //hard coded the request data + reqData.Auth = auth + reqData.Input = input + reqData.KeyID = key + + // Process the input from the request data + message, keyId, err := parseInput(reqData) + if err != nil { + log.Fatalf("Error parsing the input request data %v", err) + } + + // auth := reqData.Auth + + // Create the name with relevant data + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) + + // Get the time pre-signing + start := time.Now() + + // Do the asymmetric signing + signature, err := signAsymmetric(keyName, message) + if err != nil { + log.Fatalf("Error signing the message: %v", err) + } + + // Get the time post-signing + elapsed := time.Since(start) + + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + // Get the public key + pubKey, err := getPublicKey(keyName) + if err != nil { + log.Fatalf("error when getting public key: %v", err) + } + + resp := response{ + Signature: base64.StdEncoding.EncodeToString(signature), + PublicKey: base64.StdEncoding.EncodeToString(pubKey), + } + + return resp +} diff --git a/post-quantum-server-testing/src/falcon/falcon.go b/post-quantum-server-testing/src/falcon/falcon.go new file mode 100644 index 0000000..dc0ee64 --- /dev/null +++ b/post-quantum-server-testing/src/falcon/falcon.go @@ -0,0 +1,196 @@ +package falcon + +import ( + "crypto" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + "log" + "os" + "time" + + "github.com/joho/godotenv" + "github.com/open-quantum-safe/liboqs-go/oqs" +) + +const ( + sigName = "Falcon-512" + PublicKeySize = 897 + PrivateKeySize = 1281 +) + +// Public key struct +type PublicKey struct { + Pk []byte +} + +// Private key struct +type PrivateKey struct { + PublicKey + Sk []byte +} + +// This is a struct for the request message +type request struct { + Input string `json:"input"` + KeyID string `json:"keyid"` + Auth string `json:"auth"` +} + +// This is a struct for the output signature +type response struct { + Signature string `json:"signature"` + PublicKey string `json:"publicKey"` + //probably need a message field here +} + +// use godot package to load/read the .env file and +// return the value of the key +func GoDotEnvVariable(key string) string { + // Load .env file + err := godotenv.Load("../.env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + return os.Getenv(key) +} + +// This function processes the request struct and returns a struct of the data +func parseInput(req request) ([]byte, string, error) { + if req.Input == "" { + return nil, "", fmt.Errorf("missing the json data input") + } + if req.KeyID == "" { + return nil, "", fmt.Errorf("missing the json keyid") + } + if req.Auth == "" { + return nil, "", fmt.Errorf("missing the json auth") + } + + dcd, err := base64.StdEncoding.DecodeString(req.Input) + if err != nil { + return nil, "", fmt.Errorf("error decoding base64 input %v", err) + } + + // This returns the json []byte + return dcd, req.KeyID, nil +} + +// This function generates a pivate/public key pair +func GenerateKey() (*PrivateKey, error) { + signer := oqs.Signature{} + //defer signer.Clean() // clean up even in case of panic + + if err := signer.Init(sigName, nil); err != nil { + log.Fatal(err) + } + + pk, err := signer.GenerateKeyPair() + if err != nil { + return nil, err + } + + privateKey := new(PrivateKey) + sk := signer.ExportSecretKey() + + privateKey.PublicKey.Pk = pk + privateKey.Sk = sk + + return privateKey, err +} + +// This function takes a private key, signs a message and returns a signature +func (priv *PrivateKey) SignPQC(msg []byte) (sig []byte, err error) { + signer := oqs.Signature{} + //defer signer.Clean() + + if err := signer.Init(sigName, priv.Sk); err != nil { + return nil, fmt.Errorf("failed to init signer: %w", err) + } + + // hash with sha256 + h := sha256.New() + h.Write(msg) + hash := h.Sum(nil) + + sign, err := signer.Sign(hash) + if err != nil { + return nil, fmt.Errorf("signing failed: %w", err) + } + return sign, nil +} + +func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + return priv.SignPQC(digest) +} + +func (priv *PrivateKey) Public() crypto.PublicKey { + return &priv.PublicKey +} + +// This function verifies the signature with the public key +func Verify(pubkey *PublicKey, msg, signature []byte) bool { + var verifier = oqs.Signature{} + //defer verifier.Clean() + + if err := verifier.Init(sigName, nil); err != nil { + log.Fatal(err) + } + + isValid, err := verifier.Verify(msg, signature, pubkey.Pk) + if err != nil { + log.Fatal(err) + } + + return isValid +} + +func signData(input string, key string, auth string) response { + var reqData request + + //hard coded the request data + reqData.Auth = auth + reqData.Input = input + reqData.KeyID = key + + // Process the input from the request data + message, _, err := parseInput(reqData) + if err != nil { + log.Fatalf("Error parsing the input request data %v", err) + } + + // auth := reqData.Auth + + // Generate a private key + privKey, err := GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } + + // Get the time pre-signing + start := time.Now() + + // Do the signing with the falcon signer + signature, err := privKey.SignPQC(message) + if err != nil { + log.Fatalf("error signing message: %v", err) + } + + // Get the time post-signing + elapsed := time.Since(start) + + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + // Get the public key + pubKey := privKey.PublicKey.Pk + + resp := response{ + Signature: base64.StdEncoding.EncodeToString(signature), + PublicKey: base64.StdEncoding.EncodeToString(pubKey[:]), + } + + return resp + +} diff --git a/post-quantum-server-testing/src/mldsa/mldsa.go b/post-quantum-server-testing/src/mldsa/mldsa.go new file mode 100644 index 0000000..afe646b --- /dev/null +++ b/post-quantum-server-testing/src/mldsa/mldsa.go @@ -0,0 +1,195 @@ +package mldsa + +import ( + "context" + "crypto/sha256" + "encoding/base64" + "fmt" + "hash/crc32" + "log" + "os" + "time" + + kms "cloud.google.com/go/kms/apiv1" + kmspb "cloud.google.com/go/kms/apiv1/kmspb" + "github.com/joho/godotenv" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// This is a struct for the request message +type request struct { + Input string `json:"input"` + KeyID string `json:"keyid"` + Auth string `json:"auth"` +} + +// This is a struct for the output signature +type response struct { + Signature string `json:"signature"` + PublicKey string `json:"publicKey"` +} + +// use godot package to load/read the .env file and +// return the value of the key +func GoDotEnvVariable(key string) string { + // Load .env file + err := godotenv.Load("../.env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + return os.Getenv(key) +} + +// This function processes the request struct and returns a struct of the data +func parseInput(req request) ([]byte, string, error) { + if req.Input == "" { + return nil, "", fmt.Errorf("missing the json data input") + } + if req.KeyID == "" { + return nil, "", fmt.Errorf("missing the json keyid") + } + if req.Auth == "" { + return nil, "", fmt.Errorf("missing the json auth") + } + + dcd, err := base64.StdEncoding.DecodeString(req.Input) + if err != nil { + return nil, "", fmt.Errorf("error decoding base64 input %v", err) + } + + // This returns the json []byte + return dcd, req.KeyID, nil +} + +// getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS +func getPublicKey(name string) ([]byte, error) { + // Create the client + ctx := context.Background() + client, err := kms.NewKeyManagementClient(ctx) + if err != nil { + log.Fatalf("failed to setup client: %v", err) + } + defer client.Close() + + // Build the request + req := &kmspb.GetPublicKeyRequest{ + Name: name, + PublicKeyFormat: kmspb.PublicKey_NIST_PQC, + } + + // Call the API + result, err := client.GetPublicKey(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get public key: %w", err) + } + + // Can add integrity checks + + return result.PublicKey.Data, nil +} + +// signAsymmetric will sign a plaintext message using a saved asymmetric private +// key stored in Cloud KMS. +func signAsymmetric(name string, message []byte) ([]byte, error) { + + // Create the client. + ctx := context.Background() + client, err := kms.NewKeyManagementClient(ctx) + if err != nil { + log.Fatalf("failed to setup client: %v", err) + } + defer client.Close() + + // hash the message with sha256 + h := sha256.New() + h.Write(message) + bs := h.Sum(nil) + + plaintext := bs[:] + + // Compute data CRC32C. + crc32c := func(data []byte) uint32 { + t := crc32.MakeTable(crc32.Castagnoli) + return crc32.Checksum(data, t) + + } + + checksum := crc32c(plaintext) + + // Build the signing request. + req := &kmspb.AsymmetricSignRequest{ + Name: name, + Data: plaintext, + DataCrc32C: wrapperspb.Int64(int64(checksum)), + } + + // Call the API. + result, err := client.AsymmetricSign(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to sign digest: %w", err) + } + + // Perform integrity verification on result. + if result.Name != req.Name { + return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") + } + if int64(crc32c(result.Signature)) != result.SignatureCrc32C.Value { + return nil, fmt.Errorf("AsymmetricSign: response corrupted in-transit") + } + + //fmt.Fprintf(w, "Signed digest: %s", result.Signature) + return result.Signature, nil +} + +func signData(input string, key string, auth string) response { + var reqData request + + location := GoDotEnvVariable("LOCATION") + keyID := GoDotEnvVariable("KEY_VERSION") + keyRing := GoDotEnvVariable("KEYRING") + projectID := GoDotEnvVariable("PROJECT_ID") + + //hard coded the request data + reqData.Auth = auth + reqData.Input = input + reqData.KeyID = key + + // Process the input from the request data + message, keyId, err := parseInput(reqData) + if err != nil { + log.Fatalf("Error parsing the input request data %v", err) + } + + // auth := reqData.Auth + + // Create the name with relevant data + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) + + // Get the time pre-signing + start := time.Now() + + // Do the asymmetric signing + signature, err := signAsymmetric(keyName, message) + if err != nil { + log.Fatalf("Error signing the message: %v", err) + } + + // Get the time post-signing + elapsed := time.Since(start) + + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + // Get the public key + pubKey, err := getPublicKey(keyName) + if err != nil { + log.Fatalf("error when getting public key: %v", err) + } + + resp := response{ + Signature: base64.StdEncoding.EncodeToString(signature), + PublicKey: base64.StdEncoding.EncodeToString(pubKey), + } + + return resp +} diff --git a/post-quantum-server-testing/src/rsa/rsa.go b/post-quantum-server-testing/src/rsa/rsa.go new file mode 100644 index 0000000..b724a3e --- /dev/null +++ b/post-quantum-server-testing/src/rsa/rsa.go @@ -0,0 +1,211 @@ +package rsa + +import ( + "context" + "crypto/sha256" + "encoding/base64" + "fmt" + "hash/crc32" + "log" + "os" + "time" + + kms "cloud.google.com/go/kms/apiv1" + kmspb "cloud.google.com/go/kms/apiv1/kmspb" + "github.com/joho/godotenv" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// This is a struct for the request message +type request struct { + Input string `json:"input"` + KeyID string `json:"keyid"` + Auth string `json:"auth"` +} + +// This is a struct for the output signature +type response struct { + Signature string `json:"signature"` + PublicKey string `json:"publicKey"` +} + +// use godot package to load/read the .env file and +// return the value of the key +func GoDotEnvVariable(key string) string { + // Load .env file + err := godotenv.Load("../.env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + return os.Getenv(key) +} + +// This function processes the request struct and returns a struct of the data +func parseInput(req request) ([]byte, string, error) { + if req.Input == "" { + return nil, "", fmt.Errorf("missing the json data input") + } + if req.KeyID == "" { + return nil, "", fmt.Errorf("missing the json keyid") + } + if req.Auth == "" { + return nil, "", fmt.Errorf("missing the json auth") + } + + dcd, err := base64.StdEncoding.DecodeString(req.Input) + if err != nil { + return nil, "", fmt.Errorf("error decoding base64 input %v", err) + } + + // This returns the json []byte + return dcd, req.KeyID, nil +} + +// getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS +func getPublicKey(name string) ([]byte, error) { + // Create the client. + ctx := context.Background() + client, err := kms.NewKeyManagementClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create kms client: %w", err) + } + defer client.Close() + + // Build the request. + req := &kmspb.GetPublicKeyRequest{ + Name: name, + } + + // Call the API. + result, err := client.GetPublicKey(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get public key: %w", err) + } + + // The 'Pem' field is the raw string representation of the public key. + // Convert 'Pem' into bytes for further processing. + key := []byte(result.Pem) + + // Perform integrity verification on result. + crc32c := func(data []byte) uint32 { + t := crc32.MakeTable(crc32.Castagnoli) + return crc32.Checksum(data, t) + } + if int64(crc32c(key)) != result.PemCrc32C.Value { + return nil, fmt.Errorf("getPublicKey: response corrupted in-transit") + } + + return key, nil +} + +// signAsymmetric will sign a plaintext message using a saved asymmetric private +// key stored in Cloud KMS. +func signAsymmetric(name string, message []byte) ([]byte, error) { + // Create the client. + ctx := context.Background() + client, err := kms.NewKeyManagementClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create kms client: %w", err) + } + defer client.Close() + + // ciphertexts are always byte arrays. + plaintext := message + + // Calculate the digest of the message. + digest := sha256.New() + if _, err := digest.Write(plaintext); err != nil { + return nil, fmt.Errorf("failed to create digest: %w", err) + } + + // Compute digest's CRC32C. + crc32c := func(data []byte) uint32 { + t := crc32.MakeTable(crc32.Castagnoli) + return crc32.Checksum(data, t) + + } + digestCRC32C := crc32c(digest.Sum(nil)) + + // Build the signing request. + req := &kmspb.AsymmetricSignRequest{ + Name: name, + Digest: &kmspb.Digest{ + Digest: &kmspb.Digest_Sha256{ + Sha256: digest.Sum(nil), + }, + }, + DigestCrc32C: wrapperspb.Int64(int64(digestCRC32C)), + } + + // Call the API. + result, err := client.AsymmetricSign(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to sign digest: %w", err) + } + + // Perform integrity verification on result. + if !result.VerifiedDigestCrc32C { + return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") + } + if result.Name != req.Name { + return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") + } + if int64(crc32c(result.Signature)) != result.SignatureCrc32C.Value { + return nil, fmt.Errorf("AsymmetricSign: response corrupted in-transit") + } + + return result.Signature, nil +} + +func signData(input string, key string, auth string) response { + var reqData request + + location := GoDotEnvVariable("LOCATION") + keyID := GoDotEnvVariable("KEY_VERSION") + keyRing := GoDotEnvVariable("KEYRING") + projectID := GoDotEnvVariable("PROJECT_ID") + + //hard coded the request data + reqData.Auth = auth + reqData.Input = input + reqData.KeyID = key + + // Process the input from the request data + message, keyId, err := parseInput(reqData) + if err != nil { + log.Fatalf("Error parsing the input request data %v", err) + } + + // auth := reqData.Auth + + // Create the name with relevant data + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) + + // Get the time pre-signing + start := time.Now() + + // Do the asymmetric signing + signature, err := signAsymmetric(keyName, message) + if err != nil { + log.Fatalf("Error signing the message: %v", err) + } + + // Get the time post-signing + elapsed := time.Since(start) + + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + // Get the public key + pubKey, err := getPublicKey(keyName) + if err != nil { + log.Fatalf("error when getting public key: %v", err) + } + + resp := response{ + Signature: base64.StdEncoding.EncodeToString(signature), + PublicKey: base64.StdEncoding.EncodeToString(pubKey), + } + + return resp +} From 3627b78a41645f85f8fdd5d6735e82c495336176 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Wed, 19 Nov 2025 11:38:05 -0500 Subject: [PATCH 03/19] modified functions to get input from .env --- .../src/ecdsa/ecdsa.go | 36 ++---------------- .../src/falcon/falcon.go | 14 ------- .../src/mldsa/mldsa.go | 37 ++++--------------- post-quantum-server-testing/src/rsa/rsa.go | 34 ++--------------- 4 files changed, 13 insertions(+), 108 deletions(-) diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/ecdsa/ecdsa.go index 66af0d8..a34cc4e 100644 --- a/post-quantum-server-testing/src/ecdsa/ecdsa.go +++ b/post-quantum-server-testing/src/ecdsa/ecdsa.go @@ -7,12 +7,9 @@ import ( "fmt" "hash/crc32" "log" - "os" - "time" kms "cloud.google.com/go/kms/apiv1" kmspb "cloud.google.com/go/kms/apiv1/kmspb" - "github.com/joho/godotenv" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -29,18 +26,6 @@ type response struct { PublicKey string `json:"publicKey"` } -// use godot package to load/read the .env file and -// return the value of the key -func GoDotEnvVariable(key string) string { - // Load .env file - err := godotenv.Load("../.env") - if err != nil { - log.Fatalf("Error loading .env file") - } - - return os.Getenv(key) -} - // This function processes the request struct and returns a struct of the data func parseInput(req request) ([]byte, string, error) { if req.Input == "" { @@ -158,32 +143,22 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func signData(input string, key string, auth string) response { +func SignData(input string, key string, auth string, keyName string) response { var reqData request - location := GoDotEnvVariable("LOCATION") - keyID := GoDotEnvVariable("KEY_VERSION") - keyRing := GoDotEnvVariable("KEYRING") - projectID := GoDotEnvVariable("PROJECT_ID") - //hard coded the request data reqData.Auth = auth reqData.Input = input reqData.KeyID = key // Process the input from the request data - message, keyId, err := parseInput(reqData) + message, _, err := parseInput(reqData) if err != nil { log.Fatalf("Error parsing the input request data %v", err) } - // auth := reqData.Auth - // Create the name with relevant data - keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) - - // Get the time pre-signing - start := time.Now() + //keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) // Do the asymmetric signing signature, err := signAsymmetric(keyName, message) @@ -191,11 +166,6 @@ func signData(input string, key string, auth string) response { log.Fatalf("Error signing the message: %v", err) } - // Get the time post-signing - elapsed := time.Since(start) - - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) - // Get the public key pubKey, err := getPublicKey(keyName) if err != nil { diff --git a/post-quantum-server-testing/src/falcon/falcon.go b/post-quantum-server-testing/src/falcon/falcon.go index dc0ee64..74a42c5 100644 --- a/post-quantum-server-testing/src/falcon/falcon.go +++ b/post-quantum-server-testing/src/falcon/falcon.go @@ -7,10 +7,8 @@ import ( "fmt" "io" "log" - "os" "time" - "github.com/joho/godotenv" "github.com/open-quantum-safe/liboqs-go/oqs" ) @@ -45,18 +43,6 @@ type response struct { //probably need a message field here } -// use godot package to load/read the .env file and -// return the value of the key -func GoDotEnvVariable(key string) string { - // Load .env file - err := godotenv.Load("../.env") - if err != nil { - log.Fatalf("Error loading .env file") - } - - return os.Getenv(key) -} - // This function processes the request struct and returns a struct of the data func parseInput(req request) ([]byte, string, error) { if req.Input == "" { diff --git a/post-quantum-server-testing/src/mldsa/mldsa.go b/post-quantum-server-testing/src/mldsa/mldsa.go index afe646b..389653c 100644 --- a/post-quantum-server-testing/src/mldsa/mldsa.go +++ b/post-quantum-server-testing/src/mldsa/mldsa.go @@ -7,12 +7,9 @@ import ( "fmt" "hash/crc32" "log" - "os" - "time" kms "cloud.google.com/go/kms/apiv1" kmspb "cloud.google.com/go/kms/apiv1/kmspb" - "github.com/joho/godotenv" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -29,18 +26,6 @@ type response struct { PublicKey string `json:"publicKey"` } -// use godot package to load/read the .env file and -// return the value of the key -func GoDotEnvVariable(key string) string { - // Load .env file - err := godotenv.Load("../.env") - if err != nil { - log.Fatalf("Error loading .env file") - } - - return os.Getenv(key) -} - // This function processes the request struct and returns a struct of the data func parseInput(req request) ([]byte, string, error) { if req.Input == "" { @@ -142,13 +127,13 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func signData(input string, key string, auth string) response { +func SignData(input string, key string, auth string, keyName string) response { var reqData request - location := GoDotEnvVariable("LOCATION") - keyID := GoDotEnvVariable("KEY_VERSION") - keyRing := GoDotEnvVariable("KEYRING") - projectID := GoDotEnvVariable("PROJECT_ID") + //location := GoDotEnvVariable("LOCATION") + //keyID := GoDotEnvVariable("KEY_VERSION") + //keyRing := GoDotEnvVariable("KEYRING") + //projectID := GoDotEnvVariable("PROJECT_ID") //hard coded the request data reqData.Auth = auth @@ -156,7 +141,7 @@ func signData(input string, key string, auth string) response { reqData.KeyID = key // Process the input from the request data - message, keyId, err := parseInput(reqData) + message, _, err := parseInput(reqData) if err != nil { log.Fatalf("Error parsing the input request data %v", err) } @@ -164,10 +149,7 @@ func signData(input string, key string, auth string) response { // auth := reqData.Auth // Create the name with relevant data - keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) - - // Get the time pre-signing - start := time.Now() + //keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) // Do the asymmetric signing signature, err := signAsymmetric(keyName, message) @@ -175,11 +157,6 @@ func signData(input string, key string, auth string) response { log.Fatalf("Error signing the message: %v", err) } - // Get the time post-signing - elapsed := time.Since(start) - - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) - // Get the public key pubKey, err := getPublicKey(keyName) if err != nil { diff --git a/post-quantum-server-testing/src/rsa/rsa.go b/post-quantum-server-testing/src/rsa/rsa.go index b724a3e..77da4ce 100644 --- a/post-quantum-server-testing/src/rsa/rsa.go +++ b/post-quantum-server-testing/src/rsa/rsa.go @@ -7,12 +7,9 @@ import ( "fmt" "hash/crc32" "log" - "os" - "time" kms "cloud.google.com/go/kms/apiv1" kmspb "cloud.google.com/go/kms/apiv1/kmspb" - "github.com/joho/godotenv" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -29,18 +26,6 @@ type response struct { PublicKey string `json:"publicKey"` } -// use godot package to load/read the .env file and -// return the value of the key -func GoDotEnvVariable(key string) string { - // Load .env file - err := godotenv.Load("../.env") - if err != nil { - log.Fatalf("Error loading .env file") - } - - return os.Getenv(key) -} - // This function processes the request struct and returns a struct of the data func parseInput(req request) ([]byte, string, error) { if req.Input == "" { @@ -158,21 +143,16 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func signData(input string, key string, auth string) response { +func SignData(input string, key string, auth string, keyName string) response { var reqData request - location := GoDotEnvVariable("LOCATION") - keyID := GoDotEnvVariable("KEY_VERSION") - keyRing := GoDotEnvVariable("KEYRING") - projectID := GoDotEnvVariable("PROJECT_ID") - //hard coded the request data reqData.Auth = auth reqData.Input = input reqData.KeyID = key // Process the input from the request data - message, keyId, err := parseInput(reqData) + message, _, err := parseInput(reqData) if err != nil { log.Fatalf("Error parsing the input request data %v", err) } @@ -180,10 +160,7 @@ func signData(input string, key string, auth string) response { // auth := reqData.Auth // Create the name with relevant data - keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) - - // Get the time pre-signing - start := time.Now() + //keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) // Do the asymmetric signing signature, err := signAsymmetric(keyName, message) @@ -191,11 +168,6 @@ func signData(input string, key string, auth string) response { log.Fatalf("Error signing the message: %v", err) } - // Get the time post-signing - elapsed := time.Since(start) - - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) - // Get the public key pubKey, err := getPublicKey(keyName) if err != nil { From 17102ad54fa936c3eae5ee489c18f2bd45f1ed92 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Wed, 19 Nov 2025 11:38:51 -0500 Subject: [PATCH 04/19] added medium payload signature generation tests for all algorithms --- post-quantum-server-testing/src/main.go | 123 ++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 post-quantum-server-testing/src/main.go diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go new file mode 100644 index 0000000..4629fdc --- /dev/null +++ b/post-quantum-server-testing/src/main.go @@ -0,0 +1,123 @@ +package main + +import ( + "fmt" + "log" + "os" + "server-testing/ecdsa" + "server-testing/mldsa" + "server-testing/rsa" + "strings" + "time" + + "github.com/joho/godotenv" +) + +const ( + smallInput = "aGkK" // "hi" (<1MB) +) + +var ( + mediumInput = strings.Repeat("aGkK", 1024*512) // "hi" repeatedly (1-2MB) +) + +// use godot package to load/read the .env file and +// return the value of the key +func goDotEnvVariable(key string) string { + // Load .env file + err := godotenv.Load(".env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + return os.Getenv(key) +} + +func testMldsaSmallPayload(iterations int, location string, keyRing string, projectID string) { + + fmt.Printf("Testing Small Payload ML-DSA-65:\n") + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/mldsa/cryptoKeyVersions/1", projectID, location, keyRing) + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = mldsa.SignData(smallInput, "mldsa", "null", keyName) + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + +func testMldsaMediumPayload(iterations int, location string, keyRing string, projectID string) { + + fmt.Printf("Testing Medium Payload ML-DSA-65:\n") + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/mldsa/cryptoKeyVersions/1", projectID, location, keyRing) + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = mldsa.SignData(mediumInput, "mldsa", "null", keyName) + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + +func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string) { + + fmt.Printf("Testing Small Payload ECDSA-384:\n") + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = ecdsa.SignData(smallInput, "ecdsa", "null", keyName) + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + +func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string) { + + fmt.Printf("Testing Medium Payload ECDSA-384:\n") + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = ecdsa.SignData(mediumInput, "ecdsa", "null", keyName) + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + +func testRsaSmallPayload(iterations int, location string, keyRing string, projectID string) { + + fmt.Printf("Testing Small Payload RSA-4096:\n") + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/rsa/cryptoKeyVersions/1", projectID, location, keyRing) + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = rsa.SignData(smallInput, "rsa", "null", keyName) + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + +func main() { + iterations := 10 + location := goDotEnvVariable("LOCATION") + keyRing := goDotEnvVariable("KEYRING") + projectID := goDotEnvVariable("PROJECT_ID") + + testRsaSmallPayload(iterations, location, keyRing, projectID) +} From 0236a7bc8ad580d0a3b0201f563f143fdb99c7b6 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Mon, 15 Dec 2025 16:10:17 -0500 Subject: [PATCH 05/19] added falcon tests --- .../src/falcon/falcon.go | 12 +----------- post-quantum-server-testing/src/main.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/post-quantum-server-testing/src/falcon/falcon.go b/post-quantum-server-testing/src/falcon/falcon.go index 74a42c5..7da67ad 100644 --- a/post-quantum-server-testing/src/falcon/falcon.go +++ b/post-quantum-server-testing/src/falcon/falcon.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "log" - "time" "github.com/open-quantum-safe/liboqs-go/oqs" ) @@ -40,7 +39,6 @@ type request struct { type response struct { Signature string `json:"signature"` PublicKey string `json:"publicKey"` - //probably need a message field here } // This function processes the request struct and returns a struct of the data @@ -133,7 +131,7 @@ func Verify(pubkey *PublicKey, msg, signature []byte) bool { return isValid } -func signData(input string, key string, auth string) response { +func SignData(input string, key string, auth string) response { var reqData request //hard coded the request data @@ -155,20 +153,12 @@ func signData(input string, key string, auth string) response { log.Fatalf("Failed to generate a private key: %v", err) } - // Get the time pre-signing - start := time.Now() - // Do the signing with the falcon signer signature, err := privKey.SignPQC(message) if err != nil { log.Fatalf("error signing message: %v", err) } - // Get the time post-signing - elapsed := time.Since(start) - - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) - // Get the public key pubKey := privKey.PublicKey.Pk diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index 4629fdc..a036114 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -5,6 +5,7 @@ import ( "log" "os" "server-testing/ecdsa" + "server-testing/falcon" "server-testing/mldsa" "server-testing/rsa" "strings" @@ -65,6 +66,21 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro //fmt.Printf("Completed") } +func testFalconSmallPayload(iterations int) { + + fmt.Printf("Testing Small Payload Falcon-512:\n") + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = falcon.SignData(smallInput, "mldsa", "null") + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string) { fmt.Printf("Testing Small Payload ECDSA-384:\n") @@ -119,5 +135,6 @@ func main() { keyRing := goDotEnvVariable("KEYRING") projectID := goDotEnvVariable("PROJECT_ID") + testFalconSmallPayload(iterations) testRsaSmallPayload(iterations, location, keyRing, projectID) } From 2dd9c287ef7d473b241e6ae117b822d577d99f39 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Mon, 29 Dec 2025 16:54:14 -0500 Subject: [PATCH 06/19] added local rsa-4096 implementation --- .../src/rsa_local/rsa_local.go | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 post-quantum-server-testing/src/rsa_local/rsa_local.go diff --git a/post-quantum-server-testing/src/rsa_local/rsa_local.go b/post-quantum-server-testing/src/rsa_local/rsa_local.go new file mode 100644 index 0000000..45ea25a --- /dev/null +++ b/post-quantum-server-testing/src/rsa_local/rsa_local.go @@ -0,0 +1,80 @@ +package rsa_local + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "fmt" + "log" +) + +const ( + KeySize = 4096 +) + +// Public key struct +type PublicKey struct { + Pk *rsa.PublicKey +} + +// Private key struct +type PrivateKey struct { + PublicKey + Sk *rsa.PrivateKey +} + +// Response struct +type response struct { + Signature string `json:"signature"` + PublicKey string `json:"publicKey"` +} + +// GenerateKey generates a new RSA-4096 key pair +func GenerateKey() (*PrivateKey, error) { + sk, err := rsa.GenerateKey(rand.Reader, KeySize) + if err != nil { + return nil, err + } + pk := &sk.PublicKey + + privateKey := &PrivateKey{ + PublicKey: PublicKey{Pk: pk}, + Sk: sk, + } + return privateKey, nil +} + +// SignPQC signs a message using the private key +func (priv *PrivateKey) signPQC(msg []byte) ([]byte, error) { + hash := sha256.Sum256(msg) + sign, err := rsa.SignPSS(rand.Reader, priv.Sk, crypto.SHA256, hash[:], nil) + if err != nil { + return nil, fmt.Errorf("signing failed: %v", err) + } + return sign, nil +} + +// SignData signs input and returns a response with signature and public key +func SignData(input string, privKey *PrivateKey) response { + + message, err := base64.StdEncoding.DecodeString(input) + if err != nil { + log.Fatalf("error decoding base64 input %v", err) + } + + signature, err := privKey.signPQC(message) + if err != nil { + log.Fatalf("error signing message: %v", err) + } + + pubKey := x509.MarshalPKCS1PublicKey(privKey.PublicKey.Pk) + resp := response{ + Signature: base64.StdEncoding.EncodeToString(signature), + PublicKey: base64.StdEncoding.EncodeToString(pubKey), + } + + return resp +} From 8b3a3d27668cf4df07aed1c7f34c14b4dd86b0e6 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Mon, 29 Dec 2025 16:55:19 -0500 Subject: [PATCH 07/19] refactored signature generation code to be more efficient --- .../src/ecdsa/ecdsa.go | 22 ++--- .../src/falcon/falcon.go | 83 ++----------------- .../src/mldsa/mldsa.go | 36 +++----- post-quantum-server-testing/src/rsa/rsa.go | 2 +- 4 files changed, 24 insertions(+), 119 deletions(-) diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/ecdsa/ecdsa.go index a34cc4e..59125b5 100644 --- a/post-quantum-server-testing/src/ecdsa/ecdsa.go +++ b/post-quantum-server-testing/src/ecdsa/ecdsa.go @@ -17,7 +17,6 @@ import ( type request struct { Input string `json:"input"` KeyID string `json:"keyid"` - Auth string `json:"auth"` } // This is a struct for the output signature @@ -27,24 +26,21 @@ type response struct { } // This function processes the request struct and returns a struct of the data -func parseInput(req request) ([]byte, string, error) { +func parseInput(req request) ([]byte, error) { if req.Input == "" { - return nil, "", fmt.Errorf("missing the json data input") + return nil, fmt.Errorf("missing the json data input") } if req.KeyID == "" { - return nil, "", fmt.Errorf("missing the json keyid") - } - if req.Auth == "" { - return nil, "", fmt.Errorf("missing the json auth") + return nil, fmt.Errorf("missing the json keyid") } dcd, err := base64.StdEncoding.DecodeString(req.Input) if err != nil { - return nil, "", fmt.Errorf("error decoding base64 input %v", err) + return nil, fmt.Errorf("error decoding base64 input %v", err) } // This returns the json []byte - return dcd, req.KeyID, nil + return dcd, nil } // getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS @@ -143,23 +139,19 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func SignData(input string, key string, auth string, keyName string) response { +func SignData(input string, key string, keyName string) response { var reqData request //hard coded the request data - reqData.Auth = auth reqData.Input = input reqData.KeyID = key // Process the input from the request data - message, _, err := parseInput(reqData) + message, err := parseInput(reqData) if err != nil { log.Fatalf("Error parsing the input request data %v", err) } - // Create the name with relevant data - //keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) - // Do the asymmetric signing signature, err := signAsymmetric(keyName, message) if err != nil { diff --git a/post-quantum-server-testing/src/falcon/falcon.go b/post-quantum-server-testing/src/falcon/falcon.go index 7da67ad..1c2dd1e 100644 --- a/post-quantum-server-testing/src/falcon/falcon.go +++ b/post-quantum-server-testing/src/falcon/falcon.go @@ -1,11 +1,9 @@ package falcon import ( - "crypto" "crypto/sha256" "encoding/base64" "fmt" - "io" "log" "github.com/open-quantum-safe/liboqs-go/oqs" @@ -28,44 +26,15 @@ type PrivateKey struct { Sk []byte } -// This is a struct for the request message -type request struct { - Input string `json:"input"` - KeyID string `json:"keyid"` - Auth string `json:"auth"` -} - -// This is a struct for the output signature +// This is a struct for the response type response struct { Signature string `json:"signature"` PublicKey string `json:"publicKey"` } -// This function processes the request struct and returns a struct of the data -func parseInput(req request) ([]byte, string, error) { - if req.Input == "" { - return nil, "", fmt.Errorf("missing the json data input") - } - if req.KeyID == "" { - return nil, "", fmt.Errorf("missing the json keyid") - } - if req.Auth == "" { - return nil, "", fmt.Errorf("missing the json auth") - } - - dcd, err := base64.StdEncoding.DecodeString(req.Input) - if err != nil { - return nil, "", fmt.Errorf("error decoding base64 input %v", err) - } - - // This returns the json []byte - return dcd, req.KeyID, nil -} - // This function generates a pivate/public key pair func GenerateKey() (*PrivateKey, error) { signer := oqs.Signature{} - //defer signer.Clean() // clean up even in case of panic if err := signer.Init(sigName, nil); err != nil { log.Fatal(err) @@ -88,7 +57,6 @@ func GenerateKey() (*PrivateKey, error) { // This function takes a private key, signs a message and returns a signature func (priv *PrivateKey) SignPQC(msg []byte) (sig []byte, err error) { signer := oqs.Signature{} - //defer signer.Clean() if err := signer.Init(sigName, priv.Sk); err != nil { return nil, fmt.Errorf("failed to init signer: %w", err) @@ -106,54 +74,15 @@ func (priv *PrivateKey) SignPQC(msg []byte) (sig []byte, err error) { return sign, nil } -func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - return priv.SignPQC(digest) -} - -func (priv *PrivateKey) Public() crypto.PublicKey { - return &priv.PublicKey -} - -// This function verifies the signature with the public key -func Verify(pubkey *PublicKey, msg, signature []byte) bool { - var verifier = oqs.Signature{} - //defer verifier.Clean() - - if err := verifier.Init(sigName, nil); err != nil { - log.Fatal(err) - } - - isValid, err := verifier.Verify(msg, signature, pubkey.Pk) - if err != nil { - log.Fatal(err) - } - - return isValid -} - -func SignData(input string, key string, auth string) response { - var reqData request - - //hard coded the request data - reqData.Auth = auth - reqData.Input = input - reqData.KeyID = key - - // Process the input from the request data - message, _, err := parseInput(reqData) - if err != nil { - log.Fatalf("Error parsing the input request data %v", err) - } - - // auth := reqData.Auth +func SignData(input string, privKey *PrivateKey) response { - // Generate a private key - privKey, err := GenerateKey() + // Decode the base64 input + message, err := base64.StdEncoding.DecodeString(input) if err != nil { - log.Fatalf("Failed to generate a private key: %v", err) + log.Fatalf("error decoding base64 input %v", err) } - // Do the signing with the falcon signer + // Generate signature with Falcon signer signature, err := privKey.SignPQC(message) if err != nil { log.Fatalf("error signing message: %v", err) diff --git a/post-quantum-server-testing/src/mldsa/mldsa.go b/post-quantum-server-testing/src/mldsa/mldsa.go index 389653c..6bae9b4 100644 --- a/post-quantum-server-testing/src/mldsa/mldsa.go +++ b/post-quantum-server-testing/src/mldsa/mldsa.go @@ -17,7 +17,6 @@ import ( type request struct { Input string `json:"input"` KeyID string `json:"keyid"` - Auth string `json:"auth"` } // This is a struct for the output signature @@ -27,27 +26,24 @@ type response struct { } // This function processes the request struct and returns a struct of the data -func parseInput(req request) ([]byte, string, error) { +func parseInput(req request) ([]byte, error) { if req.Input == "" { - return nil, "", fmt.Errorf("missing the json data input") + return nil, fmt.Errorf("missing the json data input") } if req.KeyID == "" { - return nil, "", fmt.Errorf("missing the json keyid") - } - if req.Auth == "" { - return nil, "", fmt.Errorf("missing the json auth") + return nil, fmt.Errorf("missing the json keyid") } - dcd, err := base64.StdEncoding.DecodeString(req.Input) + message, err := base64.StdEncoding.DecodeString(req.Input) if err != nil { - return nil, "", fmt.Errorf("error decoding base64 input %v", err) + return nil, fmt.Errorf("error decoding base64 input %v", err) } // This returns the json []byte - return dcd, req.KeyID, nil + return message, nil } -// getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS +// getPublicKey retrieves the public key from an asymmetric key pair on Cloud KMS func getPublicKey(name string) ([]byte, error) { // Create the client ctx := context.Background() @@ -77,7 +73,6 @@ func getPublicKey(name string) ([]byte, error) { // signAsymmetric will sign a plaintext message using a saved asymmetric private // key stored in Cloud KMS. func signAsymmetric(name string, message []byte) ([]byte, error) { - // Create the client. ctx := context.Background() client, err := kms.NewKeyManagementClient(ctx) @@ -127,30 +122,19 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func SignData(input string, key string, auth string, keyName string) response { +func SignData(input string, key string, keyName string) response { var reqData request - //location := GoDotEnvVariable("LOCATION") - //keyID := GoDotEnvVariable("KEY_VERSION") - //keyRing := GoDotEnvVariable("KEYRING") - //projectID := GoDotEnvVariable("PROJECT_ID") - - //hard coded the request data - reqData.Auth = auth + // Input request data to be parsed reqData.Input = input reqData.KeyID = key // Process the input from the request data - message, _, err := parseInput(reqData) + message, err := parseInput(reqData) if err != nil { log.Fatalf("Error parsing the input request data %v", err) } - // auth := reqData.Auth - - // Create the name with relevant data - //keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) - // Do the asymmetric signing signature, err := signAsymmetric(keyName, message) if err != nil { diff --git a/post-quantum-server-testing/src/rsa/rsa.go b/post-quantum-server-testing/src/rsa/rsa.go index 77da4ce..c57bbda 100644 --- a/post-quantum-server-testing/src/rsa/rsa.go +++ b/post-quantum-server-testing/src/rsa/rsa.go @@ -47,7 +47,7 @@ func parseInput(req request) ([]byte, string, error) { return dcd, req.KeyID, nil } -// getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS +// getPublicKey retrieves the public key from an asymmetric key pair on Cloud KMS func getPublicKey(name string) ([]byte, error) { // Create the client. ctx := context.Background() From 44d9eab0a369d0befe01fc59d89a5d9cf4ffe367 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Mon, 29 Dec 2025 16:55:56 -0500 Subject: [PATCH 08/19] added small payload tests for all signature algorithms --- post-quantum-server-testing/src/main.go | 47 +++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index a036114..24e55ca 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -8,6 +8,7 @@ import ( "server-testing/falcon" "server-testing/mldsa" "server-testing/rsa" + "server-testing/rsa_local" "strings" "time" @@ -41,7 +42,7 @@ func testMldsaSmallPayload(iterations int, location string, keyRing string, proj // Get the time pre-signing start := time.Now() for range iterations { - _ = mldsa.SignData(smallInput, "mldsa", "null", keyName) + _ = mldsa.SignData(smallInput, "mldsa", keyName) } // Get the time post-signing elapsed := time.Since(start) @@ -57,7 +58,7 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro // Get the time pre-signing start := time.Now() for range iterations { - _ = mldsa.SignData(mediumInput, "mldsa", "null", keyName) + _ = mldsa.SignData(mediumInput, "mldsa", keyName) } // Get the time post-signing elapsed := time.Since(start) @@ -69,10 +70,17 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro func testFalconSmallPayload(iterations int) { fmt.Printf("Testing Small Payload Falcon-512:\n") + + // Generate a private key + privKey, err := falcon.GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } + // Get the time pre-signing start := time.Now() for range iterations { - _ = falcon.SignData(smallInput, "mldsa", "null") + _ = falcon.SignData(smallInput, privKey) } // Get the time post-signing elapsed := time.Since(start) @@ -88,7 +96,7 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj // Get the time pre-signing start := time.Now() for range iterations { - _ = ecdsa.SignData(smallInput, "ecdsa", "null", keyName) + _ = ecdsa.SignData(smallInput, "ecdsa", keyName) } // Get the time post-signing elapsed := time.Since(start) @@ -104,7 +112,7 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro // Get the time pre-signing start := time.Now() for range iterations { - _ = ecdsa.SignData(mediumInput, "ecdsa", "null", keyName) + _ = ecdsa.SignData(mediumInput, "ecdsa", keyName) } // Get the time post-signing elapsed := time.Since(start) @@ -129,12 +137,37 @@ func testRsaSmallPayload(iterations int, location string, keyRing string, projec //fmt.Printf("Completed") } +func testRsaLocalSmallPayload(iterations int) { + + fmt.Printf("Testing Small Payload RSA-4096:\n") + + // Generate a private key + privKey, err := rsa_local.GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } + + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = rsa_local.SignData(smallInput, privKey) + } + + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + func main() { - iterations := 10 + iterations := 100 location := goDotEnvVariable("LOCATION") keyRing := goDotEnvVariable("KEYRING") projectID := goDotEnvVariable("PROJECT_ID") testFalconSmallPayload(iterations) - testRsaSmallPayload(iterations, location, keyRing, projectID) + testRsaLocalSmallPayload(iterations) + testMldsaSmallPayload(iterations, location, keyRing, projectID) + testEcdsaSmallPayload(iterations, location, keyRing, projectID) } From 603f65d5735a3eeb804d525b4483c8326e53c982 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Mon, 5 Jan 2026 10:35:49 -0500 Subject: [PATCH 09/19] Changed signing operations to run in parallel --- .../src/ecdsa/ecdsa.go | 62 +--------- post-quantum-server-testing/src/main.go | 110 ++++++++++++------ .../src/mldsa/mldsa.go | 48 +------- 3 files changed, 80 insertions(+), 140 deletions(-) diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/ecdsa/ecdsa.go index 59125b5..4e591ce 100644 --- a/post-quantum-server-testing/src/ecdsa/ecdsa.go +++ b/post-quantum-server-testing/src/ecdsa/ecdsa.go @@ -19,12 +19,6 @@ type request struct { KeyID string `json:"keyid"` } -// This is a struct for the output signature -type response struct { - Signature string `json:"signature"` - PublicKey string `json:"publicKey"` -} - // This function processes the request struct and returns a struct of the data func parseInput(req request) ([]byte, error) { if req.Input == "" { @@ -34,50 +28,13 @@ func parseInput(req request) ([]byte, error) { return nil, fmt.Errorf("missing the json keyid") } - dcd, err := base64.StdEncoding.DecodeString(req.Input) + message, err := base64.StdEncoding.DecodeString(req.Input) if err != nil { return nil, fmt.Errorf("error decoding base64 input %v", err) } // This returns the json []byte - return dcd, nil -} - -// getPublicKey retrieves the public key from an asymmetric key paird on Cloud KMS -func getPublicKey(name string) ([]byte, error) { - // Create the client. - ctx := context.Background() - client, err := kms.NewKeyManagementClient(ctx) - if err != nil { - return nil, fmt.Errorf("failed to create kms client: %w", err) - } - defer client.Close() - - // Build the request. - req := &kmspb.GetPublicKeyRequest{ - Name: name, - } - - // Call the API. - result, err := client.GetPublicKey(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to get public key: %w", err) - } - - // The 'Pem' field is the raw string representation of the public key. - // Convert 'Pem' into bytes for further processing. - key := []byte(result.Pem) - - // Perform integrity verification on result. - crc32c := func(data []byte) uint32 { - t := crc32.MakeTable(crc32.Castagnoli) - return crc32.Checksum(data, t) - } - if int64(crc32c(key)) != result.PemCrc32C.Value { - return nil, fmt.Errorf("getPublicKey: response corrupted in-transit") - } - - return key, nil + return message, nil } // signAsymmetric will sign a plaintext message using a saved asymmetric private @@ -139,7 +96,7 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func SignData(input string, key string, keyName string) response { +func SignData(input string, key string, keyName string) string { var reqData request //hard coded the request data @@ -158,16 +115,5 @@ func SignData(input string, key string, keyName string) response { log.Fatalf("Error signing the message: %v", err) } - // Get the public key - pubKey, err := getPublicKey(keyName) - if err != nil { - log.Fatalf("error when getting public key: %v", err) - } - - resp := response{ - Signature: base64.StdEncoding.EncodeToString(signature), - PublicKey: base64.StdEncoding.EncodeToString(pubKey), - } - - return resp + return base64.StdEncoding.EncodeToString(signature) } diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index 24e55ca..2b3b0eb 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -7,9 +7,9 @@ import ( "server-testing/ecdsa" "server-testing/falcon" "server-testing/mldsa" - "server-testing/rsa" "server-testing/rsa_local" "strings" + "sync" "time" "github.com/joho/godotenv" @@ -26,18 +26,15 @@ var ( // use godot package to load/read the .env file and // return the value of the key func goDotEnvVariable(key string) string { - // Load .env file - err := godotenv.Load(".env") - if err != nil { - log.Fatalf("Error loading .env file") - } + // Ignored error for gcp cloud run + _ = godotenv.Load(".env") return os.Getenv(key) } -func testMldsaSmallPayload(iterations int, location string, keyRing string, projectID string) { +func testMldsaSmallPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { + defer wg.Done() - fmt.Printf("Testing Small Payload ML-DSA-65:\n") keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/mldsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() @@ -46,14 +43,14 @@ func testMldsaSmallPayload(iterations int, location string, keyRing string, proj } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload ML-DSA-65: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } -func testMldsaMediumPayload(iterations int, location string, keyRing string, projectID string) { +func testMldsaMediumPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { + defer wg.Done() - fmt.Printf("Testing Medium Payload ML-DSA-65:\n") keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/mldsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() @@ -62,14 +59,13 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload ML-DSA-65: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } -func testFalconSmallPayload(iterations int) { - - fmt.Printf("Testing Small Payload Falcon-512:\n") +func testFalconSmallPayload(iterations int, wg *sync.WaitGroup) { + defer wg.Done() // Generate a private key privKey, err := falcon.GenerateKey() @@ -84,14 +80,35 @@ func testFalconSmallPayload(iterations int) { } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload Falcon-512: %.3f ms\n", elapsed.Seconds()*1000) + + //fmt.Printf("Completed") +} + +func testFalconMediumPayload(iterations int, wg *sync.WaitGroup) { + defer wg.Done() + + // Generate a private key + privKey, err := falcon.GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } + + // Get the time pre-signing + start := time.Now() + for range iterations { + _ = falcon.SignData(mediumInput, privKey) + } + // Get the time post-signing + elapsed := time.Since(start) + fmt.Printf("Medium Payload Falcon-512: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } -func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string) { +func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { + defer wg.Done() - fmt.Printf("Testing Small Payload ECDSA-384:\n") keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() @@ -100,14 +117,14 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload ECDSA-384: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } -func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string) { +func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { + defer wg.Done() - fmt.Printf("Testing Medium Payload ECDSA-384:\n") keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() @@ -116,30 +133,35 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } -func testRsaSmallPayload(iterations int, location string, keyRing string, projectID string) { +func testRsaSmallPayload(iterations int, wg *sync.WaitGroup) { + defer wg.Done() + + // Generate a private key + privKey, err := rsa_local.GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } - fmt.Printf("Testing Small Payload RSA-4096:\n") - keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/rsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() for range iterations { - _ = rsa.SignData(smallInput, "rsa", "null", keyName) + _ = rsa_local.SignData(smallInput, privKey) } + // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload RSA-4096: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } -func testRsaLocalSmallPayload(iterations int) { - - fmt.Printf("Testing Small Payload RSA-4096:\n") +func testRsaMediumPayload(iterations int, wg *sync.WaitGroup) { + defer wg.Done() // Generate a private key privKey, err := rsa_local.GenerateKey() @@ -150,12 +172,12 @@ func testRsaLocalSmallPayload(iterations int) { // Get the time pre-signing start := time.Now() for range iterations { - _ = rsa_local.SignData(smallInput, privKey) + _ = rsa_local.SignData(mediumInput, privKey) } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Signing time is: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload RSA-4096: %.3f ms\n", elapsed.Seconds()*1000) //fmt.Printf("Completed") } @@ -165,9 +187,25 @@ func main() { location := goDotEnvVariable("LOCATION") keyRing := goDotEnvVariable("KEYRING") projectID := goDotEnvVariable("PROJECT_ID") + var wg sync.WaitGroup + + //fmt.Printf("---Running Small Payload Tests---\n\n") + wg.Add(8) + // Run the small payload tests + go testFalconSmallPayload(iterations, &wg) + go testRsaSmallPayload(iterations, &wg) + go testMldsaSmallPayload(iterations, location, keyRing, projectID, &wg) + go testEcdsaSmallPayload(iterations, location, keyRing, projectID, &wg) + + //wg.Wait() + //fmt.Printf("\n---Running Medium Payload Tests---\n\n") + //wg.Add(4) + + // Run the medium payload tests + go testFalconMediumPayload(iterations, &wg) + go testRsaMediumPayload(iterations, &wg) + go testMldsaMediumPayload(iterations, location, keyRing, projectID, &wg) + go testEcdsaMediumPayload(iterations, location, keyRing, projectID, &wg) + wg.Wait() - testFalconSmallPayload(iterations) - testRsaLocalSmallPayload(iterations) - testMldsaSmallPayload(iterations, location, keyRing, projectID) - testEcdsaSmallPayload(iterations, location, keyRing, projectID) } diff --git a/post-quantum-server-testing/src/mldsa/mldsa.go b/post-quantum-server-testing/src/mldsa/mldsa.go index 6bae9b4..a333258 100644 --- a/post-quantum-server-testing/src/mldsa/mldsa.go +++ b/post-quantum-server-testing/src/mldsa/mldsa.go @@ -19,12 +19,6 @@ type request struct { KeyID string `json:"keyid"` } -// This is a struct for the output signature -type response struct { - Signature string `json:"signature"` - PublicKey string `json:"publicKey"` -} - // This function processes the request struct and returns a struct of the data func parseInput(req request) ([]byte, error) { if req.Input == "" { @@ -43,33 +37,6 @@ func parseInput(req request) ([]byte, error) { return message, nil } -// getPublicKey retrieves the public key from an asymmetric key pair on Cloud KMS -func getPublicKey(name string) ([]byte, error) { - // Create the client - ctx := context.Background() - client, err := kms.NewKeyManagementClient(ctx) - if err != nil { - log.Fatalf("failed to setup client: %v", err) - } - defer client.Close() - - // Build the request - req := &kmspb.GetPublicKeyRequest{ - Name: name, - PublicKeyFormat: kmspb.PublicKey_NIST_PQC, - } - - // Call the API - result, err := client.GetPublicKey(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to get public key: %w", err) - } - - // Can add integrity checks - - return result.PublicKey.Data, nil -} - // signAsymmetric will sign a plaintext message using a saved asymmetric private // key stored in Cloud KMS. func signAsymmetric(name string, message []byte) ([]byte, error) { @@ -122,7 +89,7 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func SignData(input string, key string, keyName string) response { +func SignData(input string, key string, keyName string) string { var reqData request // Input request data to be parsed @@ -141,16 +108,5 @@ func SignData(input string, key string, keyName string) response { log.Fatalf("Error signing the message: %v", err) } - // Get the public key - pubKey, err := getPublicKey(keyName) - if err != nil { - log.Fatalf("error when getting public key: %v", err) - } - - resp := response{ - Signature: base64.StdEncoding.EncodeToString(signature), - PublicKey: base64.StdEncoding.EncodeToString(pubKey), - } - - return resp + return base64.StdEncoding.EncodeToString(signature) } From 25a18884fb2bc5a3e36469429de69f15156dc857 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 6 Jan 2026 13:35:58 -0500 Subject: [PATCH 10/19] changed timer to show time per operation --- post-quantum-server-testing/src/main.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index 2b3b0eb..f0040f8 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -43,7 +43,7 @@ func testMldsaSmallPayload(iterations int, location string, keyRing string, proj } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Small Payload ML-DSA-65: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -59,7 +59,7 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Medium Payload ML-DSA-65: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -80,7 +80,7 @@ func testFalconSmallPayload(iterations int, wg *sync.WaitGroup) { } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Small Payload Falcon-512: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -101,7 +101,7 @@ func testFalconMediumPayload(iterations int, wg *sync.WaitGroup) { } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Medium Payload Falcon-512: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -117,7 +117,7 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Small Payload ECDSA-384: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -133,7 +133,7 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro } // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -155,7 +155,7 @@ func testRsaSmallPayload(iterations int, wg *sync.WaitGroup) { // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Small Payload RSA-4096: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Small Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -177,7 +177,7 @@ func testRsaMediumPayload(iterations int, wg *sync.WaitGroup) { // Get the time post-signing elapsed := time.Since(start) - fmt.Printf("Medium Payload RSA-4096: %.3f ms\n", elapsed.Seconds()*1000) + fmt.Printf("Medium Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) //fmt.Printf("Completed") } @@ -189,7 +189,7 @@ func main() { projectID := goDotEnvVariable("PROJECT_ID") var wg sync.WaitGroup - //fmt.Printf("---Running Small Payload Tests---\n\n") + fmt.Printf("---Running Tests: Avg time per signature---\n\n") wg.Add(8) // Run the small payload tests go testFalconSmallPayload(iterations, &wg) From 77a2b5376ee7209c0a1f435649e509d4a066ed1e Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Mon, 12 Jan 2026 16:14:00 -0500 Subject: [PATCH 11/19] added Readme and removed extra comments from main --- post-quantum-server-testing/README | 54 ++++++ post-quantum-server-testing/src/main.go | 23 +-- post-quantum-server-testing/src/rsa/rsa.go | 183 --------------------- 3 files changed, 57 insertions(+), 203 deletions(-) create mode 100644 post-quantum-server-testing/README delete mode 100644 post-quantum-server-testing/src/rsa/rsa.go diff --git a/post-quantum-server-testing/README b/post-quantum-server-testing/README new file mode 100644 index 0000000..f900bc7 --- /dev/null +++ b/post-quantum-server-testing/README @@ -0,0 +1,54 @@ +# Post-Quantum Signature Generation Performance Test + +Benchmark signature generation performance for post-quantum algorithms ML-DSA-65, Falcon-512 and current algorithms RSA-4096, ECDSA-384. + +This is to help us investigate and understand performance issues we might run into while doing code-signing and content-signing. + +The test will execute 100 signature verifications for 4 different algorithms and 2 different payload sizes. Then return the signing time per operation of each algorithm. + +## Running the Testing Program + +This program is meant to be a server-side test and can only be tested by authenticated GCP users, otherwise KMS signing will not work. + + +### Linux (Ubuntu/Debian) + +#### 1. Install Dependencies + +```bash +sudo apt update && sudo apt-get install -y \ + git \ + cmake \ + gcc \ + pkg-config \ + libssl-dev +``` + +#### 2. Configure Environment Variables + +Create a `.env` file in the `src` directory with the Google KMS details: + +```bash +LOCATION=global +KEYRING=your-keyring +PROJECT_ID=your-gcp-project-id +``` + +#### 3. Authenticate with GCP + +```bash +gcloud auth application-default login +``` + +#### 4. Install Go Dependencies + +```bash +cd post-quantum-server-testing/src +go mod download +``` + +#### 5. Run the Tests + +```bash +go run main.go +``` diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index f0040f8..eca9b56 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -44,8 +44,6 @@ func testMldsaSmallPayload(iterations int, location string, keyRing string, proj // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testMldsaMediumPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { @@ -60,8 +58,6 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testFalconSmallPayload(iterations int, wg *sync.WaitGroup) { @@ -81,8 +77,6 @@ func testFalconSmallPayload(iterations int, wg *sync.WaitGroup) { // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testFalconMediumPayload(iterations int, wg *sync.WaitGroup) { @@ -102,8 +96,6 @@ func testFalconMediumPayload(iterations int, wg *sync.WaitGroup) { // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { @@ -118,8 +110,6 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { @@ -134,8 +124,6 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testRsaSmallPayload(iterations int, wg *sync.WaitGroup) { @@ -156,8 +144,6 @@ func testRsaSmallPayload(iterations int, wg *sync.WaitGroup) { // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func testRsaMediumPayload(iterations int, wg *sync.WaitGroup) { @@ -178,8 +164,6 @@ func testRsaMediumPayload(iterations int, wg *sync.WaitGroup) { // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) - - //fmt.Printf("Completed") } func main() { @@ -190,17 +174,16 @@ func main() { var wg sync.WaitGroup fmt.Printf("---Running Tests: Avg time per signature---\n\n") + + // Create a workgroup for 8 functions wg.Add(8) + // Run the small payload tests go testFalconSmallPayload(iterations, &wg) go testRsaSmallPayload(iterations, &wg) go testMldsaSmallPayload(iterations, location, keyRing, projectID, &wg) go testEcdsaSmallPayload(iterations, location, keyRing, projectID, &wg) - //wg.Wait() - //fmt.Printf("\n---Running Medium Payload Tests---\n\n") - //wg.Add(4) - // Run the medium payload tests go testFalconMediumPayload(iterations, &wg) go testRsaMediumPayload(iterations, &wg) diff --git a/post-quantum-server-testing/src/rsa/rsa.go b/post-quantum-server-testing/src/rsa/rsa.go deleted file mode 100644 index c57bbda..0000000 --- a/post-quantum-server-testing/src/rsa/rsa.go +++ /dev/null @@ -1,183 +0,0 @@ -package rsa - -import ( - "context" - "crypto/sha256" - "encoding/base64" - "fmt" - "hash/crc32" - "log" - - kms "cloud.google.com/go/kms/apiv1" - kmspb "cloud.google.com/go/kms/apiv1/kmspb" - "google.golang.org/protobuf/types/known/wrapperspb" -) - -// This is a struct for the request message -type request struct { - Input string `json:"input"` - KeyID string `json:"keyid"` - Auth string `json:"auth"` -} - -// This is a struct for the output signature -type response struct { - Signature string `json:"signature"` - PublicKey string `json:"publicKey"` -} - -// This function processes the request struct and returns a struct of the data -func parseInput(req request) ([]byte, string, error) { - if req.Input == "" { - return nil, "", fmt.Errorf("missing the json data input") - } - if req.KeyID == "" { - return nil, "", fmt.Errorf("missing the json keyid") - } - if req.Auth == "" { - return nil, "", fmt.Errorf("missing the json auth") - } - - dcd, err := base64.StdEncoding.DecodeString(req.Input) - if err != nil { - return nil, "", fmt.Errorf("error decoding base64 input %v", err) - } - - // This returns the json []byte - return dcd, req.KeyID, nil -} - -// getPublicKey retrieves the public key from an asymmetric key pair on Cloud KMS -func getPublicKey(name string) ([]byte, error) { - // Create the client. - ctx := context.Background() - client, err := kms.NewKeyManagementClient(ctx) - if err != nil { - return nil, fmt.Errorf("failed to create kms client: %w", err) - } - defer client.Close() - - // Build the request. - req := &kmspb.GetPublicKeyRequest{ - Name: name, - } - - // Call the API. - result, err := client.GetPublicKey(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to get public key: %w", err) - } - - // The 'Pem' field is the raw string representation of the public key. - // Convert 'Pem' into bytes for further processing. - key := []byte(result.Pem) - - // Perform integrity verification on result. - crc32c := func(data []byte) uint32 { - t := crc32.MakeTable(crc32.Castagnoli) - return crc32.Checksum(data, t) - } - if int64(crc32c(key)) != result.PemCrc32C.Value { - return nil, fmt.Errorf("getPublicKey: response corrupted in-transit") - } - - return key, nil -} - -// signAsymmetric will sign a plaintext message using a saved asymmetric private -// key stored in Cloud KMS. -func signAsymmetric(name string, message []byte) ([]byte, error) { - // Create the client. - ctx := context.Background() - client, err := kms.NewKeyManagementClient(ctx) - if err != nil { - return nil, fmt.Errorf("failed to create kms client: %w", err) - } - defer client.Close() - - // ciphertexts are always byte arrays. - plaintext := message - - // Calculate the digest of the message. - digest := sha256.New() - if _, err := digest.Write(plaintext); err != nil { - return nil, fmt.Errorf("failed to create digest: %w", err) - } - - // Compute digest's CRC32C. - crc32c := func(data []byte) uint32 { - t := crc32.MakeTable(crc32.Castagnoli) - return crc32.Checksum(data, t) - - } - digestCRC32C := crc32c(digest.Sum(nil)) - - // Build the signing request. - req := &kmspb.AsymmetricSignRequest{ - Name: name, - Digest: &kmspb.Digest{ - Digest: &kmspb.Digest_Sha256{ - Sha256: digest.Sum(nil), - }, - }, - DigestCrc32C: wrapperspb.Int64(int64(digestCRC32C)), - } - - // Call the API. - result, err := client.AsymmetricSign(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to sign digest: %w", err) - } - - // Perform integrity verification on result. - if !result.VerifiedDigestCrc32C { - return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") - } - if result.Name != req.Name { - return nil, fmt.Errorf("AsymmetricSign: request corrupted in-transit") - } - if int64(crc32c(result.Signature)) != result.SignatureCrc32C.Value { - return nil, fmt.Errorf("AsymmetricSign: response corrupted in-transit") - } - - return result.Signature, nil -} - -func SignData(input string, key string, auth string, keyName string) response { - var reqData request - - //hard coded the request data - reqData.Auth = auth - reqData.Input = input - reqData.KeyID = key - - // Process the input from the request data - message, _, err := parseInput(reqData) - if err != nil { - log.Fatalf("Error parsing the input request data %v", err) - } - - // auth := reqData.Auth - - // Create the name with relevant data - //keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/%s", projectID, location, keyRing, keyId, keyID) - - // Do the asymmetric signing - signature, err := signAsymmetric(keyName, message) - if err != nil { - log.Fatalf("Error signing the message: %v", err) - } - - // Get the public key - pubKey, err := getPublicKey(keyName) - if err != nil { - log.Fatalf("error when getting public key: %v", err) - } - - resp := response{ - Signature: base64.StdEncoding.EncodeToString(signature), - PublicKey: base64.StdEncoding.EncodeToString(pubKey), - } - - return resp -} From e23499d5992c7d333d9be0612b2b6eff9f6a20ed Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 13 Jan 2026 12:08:18 -0500 Subject: [PATCH 12/19] added comments for clarity --- post-quantum-server-testing/src/ecdsa/ecdsa.go | 1 + post-quantum-server-testing/src/main.go | 2 +- post-quantum-server-testing/src/mldsa/mldsa.go | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/ecdsa/ecdsa.go index 4e591ce..e5e8fdf 100644 --- a/post-quantum-server-testing/src/ecdsa/ecdsa.go +++ b/post-quantum-server-testing/src/ecdsa/ecdsa.go @@ -96,6 +96,7 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } +// This function signs the data and and returns the signature as a base64 string func SignData(input string, key string, keyName string) string { var reqData request diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index eca9b56..c56feb3 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -175,7 +175,6 @@ func main() { fmt.Printf("---Running Tests: Avg time per signature---\n\n") - // Create a workgroup for 8 functions wg.Add(8) // Run the small payload tests @@ -189,6 +188,7 @@ func main() { go testRsaMediumPayload(iterations, &wg) go testMldsaMediumPayload(iterations, location, keyRing, projectID, &wg) go testEcdsaMediumPayload(iterations, location, keyRing, projectID, &wg) + wg.Wait() } diff --git a/post-quantum-server-testing/src/mldsa/mldsa.go b/post-quantum-server-testing/src/mldsa/mldsa.go index a333258..3656384 100644 --- a/post-quantum-server-testing/src/mldsa/mldsa.go +++ b/post-quantum-server-testing/src/mldsa/mldsa.go @@ -19,7 +19,7 @@ type request struct { KeyID string `json:"keyid"` } -// This function processes the request struct and returns a struct of the data +// This function processes the request struct and returns the byte message func parseInput(req request) ([]byte, error) { if req.Input == "" { return nil, fmt.Errorf("missing the json data input") @@ -85,7 +85,6 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return nil, fmt.Errorf("AsymmetricSign: response corrupted in-transit") } - //fmt.Fprintf(w, "Signed digest: %s", result.Signature) return result.Signature, nil } From a5e47d5e8eeaf8e5c412de868aa474ee14c2867d Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 13 Jan 2026 12:16:25 -0500 Subject: [PATCH 13/19] updated README --- post-quantum-server-testing/README | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/post-quantum-server-testing/README b/post-quantum-server-testing/README index f900bc7..8a79ffe 100644 --- a/post-quantum-server-testing/README +++ b/post-quantum-server-testing/README @@ -16,10 +16,11 @@ This program is meant to be a server-side test and can only be tested by authent #### 1. Install Dependencies ```bash -sudo apt update && sudo apt-get install -y \ +sudo apt update +sudo apt install -y \ git \ cmake \ - gcc \ + build-essential \ pkg-config \ libssl-dev ``` From 5401592370f42caf618844221270b3ff86e754eb Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 13 Jan 2026 12:17:20 -0500 Subject: [PATCH 14/19] updated README --- post-quantum-server-testing/README | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/post-quantum-server-testing/README b/post-quantum-server-testing/README index 8a79ffe..62afd01 100644 --- a/post-quantum-server-testing/README +++ b/post-quantum-server-testing/README @@ -4,13 +4,12 @@ Benchmark signature generation performance for post-quantum algorithms ML-DSA-65 This is to help us investigate and understand performance issues we might run into while doing code-signing and content-signing. -The test will execute 100 signature verifications for 4 different algorithms and 2 different payload sizes. Then return the signing time per operation of each algorithm. +The test will execute 100 signature generations for 4 different algorithms each with 2 different payload sizes. Then return the signing time per operation of each algorithm. ## Running the Testing Program This program is meant to be a server-side test and can only be tested by authenticated GCP users, otherwise KMS signing will not work. - ### Linux (Ubuntu/Debian) #### 1. Install Dependencies From f649fec759ef7ef2a681af3a01c765e2a6b7af3a Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Wed, 14 Jan 2026 12:07:37 -0500 Subject: [PATCH 15/19] updated README with new build intructions --- .../{README => README.md} | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) rename post-quantum-server-testing/{README => README.md} (50%) diff --git a/post-quantum-server-testing/README b/post-quantum-server-testing/README.md similarity index 50% rename from post-quantum-server-testing/README rename to post-quantum-server-testing/README.md index 62afd01..e82bbe4 100644 --- a/post-quantum-server-testing/README +++ b/post-quantum-server-testing/README.md @@ -21,12 +21,13 @@ sudo apt install -y \ cmake \ build-essential \ pkg-config \ - libssl-dev + libssl-dev \ + golang-go ``` #### 2. Configure Environment Variables -Create a `.env` file in the `src` directory with the Google KMS details: +Create a `.env` file in the `src` directory with your own Google KMS details: ```bash LOCATION=global @@ -34,20 +35,48 @@ KEYRING=your-keyring PROJECT_ID=your-gcp-project-id ``` -#### 3. Authenticate with GCP +#### 3. Build and Install Liboqs + +```bash +# Clone and install the liboqs using cmake +git clone --depth=1 https://github.com/open-quantum-safe/liboqs +cmake -S liboqs -B liboqs/build -DBUILD_SHARED_LIBS=ON +cmake --build liboqs/build --parallel 4 +sudo cmake --build liboqs/build --target install +``` +**Note:** Change `--parallel 4` to the amount of available cores on your system. + +#### 4. Set Up liboqs-go Wrapper + +```bash +# Clone the liboqs-go +git clone --depth=1 https://github.com/open-quantum-safe/liboqs-go + +# Next, you must modify the following lines in $HOME/liboqs-go/.config/liboqs-go.pc +LIBOQS_INCLUDE_DIR=/usr/local/include +LIBOQS_LIB_DIR=/usr/local/lib + +# Add these environment variables +export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$PWD/liboqs-go/.config +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib +``` + +**Note:** The `liboqs-go.pc` might already have the lines added + +#### 5. Authenticate with GCP ```bash gcloud auth application-default login ``` -#### 4. Install Go Dependencies +#### 6. Install Go Dependencies ```bash cd post-quantum-server-testing/src go mod download ``` -#### 5. Run the Tests +#### 7. Run the Program ```bash go run main.go From 5d125068700f130ad5ce64408bfa57f0ab863944 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Wed, 14 Jan 2026 15:25:51 -0500 Subject: [PATCH 16/19] added parallel execution for KMS signing and updated gitignore --- post-quantum-server-testing/src/.gitignore | 22 +++ .../src/ecdsa/ecdsa.go | 5 +- .../src/falcon/falcon.go | 4 +- post-quantum-server-testing/src/main.go | 152 ++++++++++++------ .../src/mldsa/mldsa.go | 5 +- .../src/rsa_local/rsa_local.go | 4 +- 6 files changed, 135 insertions(+), 57 deletions(-) create mode 100644 post-quantum-server-testing/src/.gitignore diff --git a/post-quantum-server-testing/src/.gitignore b/post-quantum-server-testing/src/.gitignore new file mode 100644 index 0000000..fb4099e --- /dev/null +++ b/post-quantum-server-testing/src/.gitignore @@ -0,0 +1,22 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Code coverage profiles and other test artifacts +*.out +coverage.* +*.coverprofile +profile.cov + +# Go workspace file +go.work +go.work.sum + +# env file +.env diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/ecdsa/ecdsa.go index e5e8fdf..e5b4029 100644 --- a/post-quantum-server-testing/src/ecdsa/ecdsa.go +++ b/post-quantum-server-testing/src/ecdsa/ecdsa.go @@ -7,6 +7,7 @@ import ( "fmt" "hash/crc32" "log" + "sync" kms "cloud.google.com/go/kms/apiv1" kmspb "cloud.google.com/go/kms/apiv1/kmspb" @@ -97,7 +98,9 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { } // This function signs the data and and returns the signature as a base64 string -func SignData(input string, key string, keyName string) string { +func SignData(input string, key string, keyName string, wg *sync.WaitGroup) string { + defer wg.Done() + var reqData request //hard coded the request data diff --git a/post-quantum-server-testing/src/falcon/falcon.go b/post-quantum-server-testing/src/falcon/falcon.go index 1c2dd1e..0a497b0 100644 --- a/post-quantum-server-testing/src/falcon/falcon.go +++ b/post-quantum-server-testing/src/falcon/falcon.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "fmt" "log" + "sync" "github.com/open-quantum-safe/liboqs-go/oqs" ) @@ -74,7 +75,8 @@ func (priv *PrivateKey) SignPQC(msg []byte) (sig []byte, err error) { return sign, nil } -func SignData(input string, privKey *PrivateKey) response { +func SignData(input string, privKey *PrivateKey, wg *sync.WaitGroup) response { + defer wg.Done() // Decode the base64 input message, err := base64.StdEncoding.DecodeString(input) diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index c56feb3..24caf45 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -32,37 +32,49 @@ func goDotEnvVariable(key string) string { return os.Getenv(key) } -func testMldsaSmallPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { - defer wg.Done() - +func testMldsaSmallPayload(iterations int, location string, keyRing string, projectID string) { keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/mldsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() - for range iterations { - _ = mldsa.SignData(smallInput, "mldsa", keyName) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = mldsa.SignData(smallInput, "mldsa", keyName, &wg) + }(i) } + wg.Wait() + // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testMldsaMediumPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { - defer wg.Done() - +func testMldsaMediumPayload(iterations int, location string, keyRing string, projectID string) { keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/mldsa/cryptoKeyVersions/1", projectID, location, keyRing) + // Get the time pre-signing start := time.Now() - for range iterations { - _ = mldsa.SignData(mediumInput, "mldsa", keyName) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = mldsa.SignData(mediumInput, "mldsa", keyName, &wg) + }(i) } + wg.Wait() // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testFalconSmallPayload(iterations int, wg *sync.WaitGroup) { - defer wg.Done() - +func testFalconSmallPayload(iterations int) { // Generate a private key privKey, err := falcon.GenerateKey() if err != nil { @@ -71,17 +83,23 @@ func testFalconSmallPayload(iterations int, wg *sync.WaitGroup) { // Get the time pre-signing start := time.Now() - for range iterations { - _ = falcon.SignData(smallInput, privKey) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = falcon.SignData(smallInput, privKey, &wg) + }(i) } + wg.Wait() // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testFalconMediumPayload(iterations int, wg *sync.WaitGroup) { - defer wg.Done() - +func testFalconMediumPayload(iterations int) { // Generate a private key privKey, err := falcon.GenerateKey() if err != nil { @@ -90,45 +108,64 @@ func testFalconMediumPayload(iterations int, wg *sync.WaitGroup) { // Get the time pre-signing start := time.Now() - for range iterations { - _ = falcon.SignData(mediumInput, privKey) + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = falcon.SignData(mediumInput, privKey, &wg) + }(i) } + wg.Wait() // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { - defer wg.Done() - +func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string) { keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() - for range iterations { - _ = ecdsa.SignData(smallInput, "ecdsa", keyName) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = ecdsa.SignData(smallInput, "ecdsa", keyName, &wg) + }(i) } + wg.Wait() + // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string, wg *sync.WaitGroup) { - defer wg.Done() - +func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string) { keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() - for range iterations { - _ = ecdsa.SignData(mediumInput, "ecdsa", keyName) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = ecdsa.SignData(mediumInput, "ecdsa", keyName, &wg) + }(i) } + wg.Wait() + // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testRsaSmallPayload(iterations int, wg *sync.WaitGroup) { - defer wg.Done() - +func testRsaSmallPayload(iterations int) { // Generate a private key privKey, err := rsa_local.GenerateKey() if err != nil { @@ -137,18 +174,24 @@ func testRsaSmallPayload(iterations int, wg *sync.WaitGroup) { // Get the time pre-signing start := time.Now() - for range iterations { - _ = rsa_local.SignData(smallInput, privKey) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = rsa_local.SignData(smallInput, privKey, &wg) + }(i) } + wg.Wait() // Get the time post-signing elapsed := time.Since(start) fmt.Printf("Small Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testRsaMediumPayload(iterations int, wg *sync.WaitGroup) { - defer wg.Done() - +func testRsaMediumPayload(iterations int) { // Generate a private key privKey, err := rsa_local.GenerateKey() if err != nil { @@ -157,9 +200,17 @@ func testRsaMediumPayload(iterations int, wg *sync.WaitGroup) { // Get the time pre-signing start := time.Now() - for range iterations { - _ = rsa_local.SignData(mediumInput, privKey) + + // Create a waitgroup + var wg sync.WaitGroup + wg.Add(iterations) + + for i := 0; i < iterations; i++ { + go func(j int) { + _ = rsa_local.SignData(mediumInput, privKey, &wg) + }(i) } + wg.Wait() // Get the time post-signing elapsed := time.Since(start) @@ -171,24 +222,19 @@ func main() { location := goDotEnvVariable("LOCATION") keyRing := goDotEnvVariable("KEYRING") projectID := goDotEnvVariable("PROJECT_ID") - var wg sync.WaitGroup fmt.Printf("---Running Tests: Avg time per signature---\n\n") - wg.Add(8) - // Run the small payload tests - go testFalconSmallPayload(iterations, &wg) - go testRsaSmallPayload(iterations, &wg) - go testMldsaSmallPayload(iterations, location, keyRing, projectID, &wg) - go testEcdsaSmallPayload(iterations, location, keyRing, projectID, &wg) + testFalconSmallPayload(iterations) + testRsaSmallPayload(iterations) + testMldsaSmallPayload(iterations, location, keyRing, projectID) + testEcdsaSmallPayload(iterations, location, keyRing, projectID) // Run the medium payload tests - go testFalconMediumPayload(iterations, &wg) - go testRsaMediumPayload(iterations, &wg) - go testMldsaMediumPayload(iterations, location, keyRing, projectID, &wg) - go testEcdsaMediumPayload(iterations, location, keyRing, projectID, &wg) - - wg.Wait() + testFalconMediumPayload(iterations) + testRsaMediumPayload(iterations) + testMldsaMediumPayload(iterations, location, keyRing, projectID) + testEcdsaMediumPayload(iterations, location, keyRing, projectID) } diff --git a/post-quantum-server-testing/src/mldsa/mldsa.go b/post-quantum-server-testing/src/mldsa/mldsa.go index 3656384..b6e4c83 100644 --- a/post-quantum-server-testing/src/mldsa/mldsa.go +++ b/post-quantum-server-testing/src/mldsa/mldsa.go @@ -7,6 +7,7 @@ import ( "fmt" "hash/crc32" "log" + "sync" kms "cloud.google.com/go/kms/apiv1" kmspb "cloud.google.com/go/kms/apiv1/kmspb" @@ -88,7 +89,9 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { return result.Signature, nil } -func SignData(input string, key string, keyName string) string { +func SignData(input string, key string, keyName string, wg *sync.WaitGroup) string { + defer wg.Done() + var reqData request // Input request data to be parsed diff --git a/post-quantum-server-testing/src/rsa_local/rsa_local.go b/post-quantum-server-testing/src/rsa_local/rsa_local.go index 45ea25a..53e85a8 100644 --- a/post-quantum-server-testing/src/rsa_local/rsa_local.go +++ b/post-quantum-server-testing/src/rsa_local/rsa_local.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "fmt" "log" + "sync" ) const ( @@ -58,7 +59,8 @@ func (priv *PrivateKey) signPQC(msg []byte) ([]byte, error) { } // SignData signs input and returns a response with signature and public key -func SignData(input string, privKey *PrivateKey) response { +func SignData(input string, privKey *PrivateKey, wg *sync.WaitGroup) response { + defer wg.Done() message, err := base64.StdEncoding.DecodeString(input) if err != nil { From cddcb6098acbbd9eeb0f542977a36067245b8031 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Thu, 15 Jan 2026 16:40:48 -0500 Subject: [PATCH 17/19] updated README with KMS key name info --- post-quantum-server-testing/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/post-quantum-server-testing/README.md b/post-quantum-server-testing/README.md index e82bbe4..a60c631 100644 --- a/post-quantum-server-testing/README.md +++ b/post-quantum-server-testing/README.md @@ -8,8 +8,12 @@ The test will execute 100 signature generations for 4 different algorithms each ## Running the Testing Program +### Requirements + This program is meant to be a server-side test and can only be tested by authenticated GCP users, otherwise KMS signing will not work. +**Note:** The ML-DSA-65 and ECDSA-384 test functions use KMS key names `mldsa` and `ecdsa`. If your KMS keys have different names, you'll need to update the key names in the `SignData()` function calls in `main.go`. + ### Linux (Ubuntu/Debian) #### 1. Install Dependencies @@ -35,7 +39,7 @@ KEYRING=your-keyring PROJECT_ID=your-gcp-project-id ``` -#### 3. Build and Install Liboqs +#### 3. Build and Install liboqs ```bash # Clone and install the liboqs using cmake From b56be25270b06640eea118e43830cce66471d01d Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Thu, 15 Jan 2026 16:42:01 -0500 Subject: [PATCH 18/19] Removed redundant key generations and uppercase constant variables --- .../src/falcon/falcon.go | 10 +-- post-quantum-server-testing/src/main.go | 73 ++++++++----------- .../src/rsa_local/rsa_local.go | 4 +- 3 files changed, 38 insertions(+), 49 deletions(-) diff --git a/post-quantum-server-testing/src/falcon/falcon.go b/post-quantum-server-testing/src/falcon/falcon.go index 0a497b0..08b5646 100644 --- a/post-quantum-server-testing/src/falcon/falcon.go +++ b/post-quantum-server-testing/src/falcon/falcon.go @@ -11,9 +11,9 @@ import ( ) const ( - sigName = "Falcon-512" - PublicKeySize = 897 - PrivateKeySize = 1281 + SIGNAME = "Falcon-512" + PUBLICKEYSIZE = 897 + PRIVATEKEYSIZE = 1281 ) // Public key struct @@ -37,7 +37,7 @@ type response struct { func GenerateKey() (*PrivateKey, error) { signer := oqs.Signature{} - if err := signer.Init(sigName, nil); err != nil { + if err := signer.Init(SIGNAME, nil); err != nil { log.Fatal(err) } @@ -59,7 +59,7 @@ func GenerateKey() (*PrivateKey, error) { func (priv *PrivateKey) SignPQC(msg []byte) (sig []byte, err error) { signer := oqs.Signature{} - if err := signer.Init(sigName, priv.Sk); err != nil { + if err := signer.Init(SIGNAME, priv.Sk); err != nil { return nil, fmt.Errorf("failed to init signer: %w", err) } diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index 24caf45..c2b4888 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -16,11 +16,11 @@ import ( ) const ( - smallInput = "aGkK" // "hi" (<1MB) + SMALLINPUT = "aGkK" // "hi" (<1MB) ) var ( - mediumInput = strings.Repeat("aGkK", 1024*512) // "hi" repeatedly (1-2MB) + MEDIUMINPUT = strings.Repeat("aGkK", 1024*512) // "hi" repeatedly (1-2MB) ) // use godot package to load/read the .env file and @@ -43,7 +43,7 @@ func testMldsaSmallPayload(iterations int, location string, keyRing string, proj for i := 0; i < iterations; i++ { go func(j int) { - _ = mldsa.SignData(smallInput, "mldsa", keyName, &wg) + _ = mldsa.SignData(SMALLINPUT, "mldsa", keyName, &wg) }(i) } wg.Wait() @@ -65,7 +65,7 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro for i := 0; i < iterations; i++ { go func(j int) { - _ = mldsa.SignData(mediumInput, "mldsa", keyName, &wg) + _ = mldsa.SignData(MEDIUMINPUT, "mldsa", keyName, &wg) }(i) } wg.Wait() @@ -74,13 +74,7 @@ func testMldsaMediumPayload(iterations int, location string, keyRing string, pro fmt.Printf("Medium Payload ML-DSA-65: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testFalconSmallPayload(iterations int) { - // Generate a private key - privKey, err := falcon.GenerateKey() - if err != nil { - log.Fatalf("Failed to generate a private key: %v", err) - } - +func testFalconSmallPayload(iterations int, privKey *falcon.PrivateKey) { // Get the time pre-signing start := time.Now() @@ -90,7 +84,7 @@ func testFalconSmallPayload(iterations int) { for i := 0; i < iterations; i++ { go func(j int) { - _ = falcon.SignData(smallInput, privKey, &wg) + _ = falcon.SignData(SMALLINPUT, privKey, &wg) }(i) } wg.Wait() @@ -99,22 +93,17 @@ func testFalconSmallPayload(iterations int) { fmt.Printf("Small Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testFalconMediumPayload(iterations int) { - // Generate a private key - privKey, err := falcon.GenerateKey() - if err != nil { - log.Fatalf("Failed to generate a private key: %v", err) - } - +func testFalconMediumPayload(iterations int, privKey *falcon.PrivateKey) { // Get the time pre-signing start := time.Now() + // Create a waitgroup var wg sync.WaitGroup wg.Add(iterations) for i := 0; i < iterations; i++ { go func(j int) { - _ = falcon.SignData(mediumInput, privKey, &wg) + _ = falcon.SignData(MEDIUMINPUT, privKey, &wg) }(i) } wg.Wait() @@ -134,7 +123,7 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj for i := 0; i < iterations; i++ { go func(j int) { - _ = ecdsa.SignData(smallInput, "ecdsa", keyName, &wg) + _ = ecdsa.SignData(SMALLINPUT, "ecdsa", keyName, &wg) }(i) } wg.Wait() @@ -155,7 +144,7 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro for i := 0; i < iterations; i++ { go func(j int) { - _ = ecdsa.SignData(mediumInput, "ecdsa", keyName, &wg) + _ = ecdsa.SignData(MEDIUMINPUT, "ecdsa", keyName, &wg) }(i) } wg.Wait() @@ -165,13 +154,7 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testRsaSmallPayload(iterations int) { - // Generate a private key - privKey, err := rsa_local.GenerateKey() - if err != nil { - log.Fatalf("Failed to generate a private key: %v", err) - } - +func testRsaSmallPayload(iterations int, privKey *rsa_local.PrivateKey) { // Get the time pre-signing start := time.Now() @@ -181,7 +164,7 @@ func testRsaSmallPayload(iterations int) { for i := 0; i < iterations; i++ { go func(j int) { - _ = rsa_local.SignData(smallInput, privKey, &wg) + _ = rsa_local.SignData(SMALLINPUT, privKey, &wg) }(i) } wg.Wait() @@ -191,13 +174,7 @@ func testRsaSmallPayload(iterations int) { fmt.Printf("Small Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testRsaMediumPayload(iterations int) { - // Generate a private key - privKey, err := rsa_local.GenerateKey() - if err != nil { - log.Fatalf("Failed to generate a private key: %v", err) - } - +func testRsaMediumPayload(iterations int, privKey *rsa_local.PrivateKey) { // Get the time pre-signing start := time.Now() @@ -207,7 +184,7 @@ func testRsaMediumPayload(iterations int) { for i := 0; i < iterations; i++ { go func(j int) { - _ = rsa_local.SignData(mediumInput, privKey, &wg) + _ = rsa_local.SignData(MEDIUMINPUT, privKey, &wg) }(i) } wg.Wait() @@ -223,17 +200,29 @@ func main() { keyRing := goDotEnvVariable("KEYRING") projectID := goDotEnvVariable("PROJECT_ID") + // Generate a falcon private key + privKeyFalcon, err := falcon.GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } + + // Generate a rsa private key + privKeyRsa, err := rsa_local.GenerateKey() + if err != nil { + log.Fatalf("Failed to generate a private key: %v", err) + } + fmt.Printf("---Running Tests: Avg time per signature---\n\n") // Run the small payload tests - testFalconSmallPayload(iterations) - testRsaSmallPayload(iterations) + testFalconSmallPayload(iterations, privKeyFalcon) + testRsaSmallPayload(iterations, privKeyRsa) testMldsaSmallPayload(iterations, location, keyRing, projectID) testEcdsaSmallPayload(iterations, location, keyRing, projectID) // Run the medium payload tests - testFalconMediumPayload(iterations) - testRsaMediumPayload(iterations) + testFalconMediumPayload(iterations, privKeyFalcon) + testRsaMediumPayload(iterations, privKeyRsa) testMldsaMediumPayload(iterations, location, keyRing, projectID) testEcdsaMediumPayload(iterations, location, keyRing, projectID) diff --git a/post-quantum-server-testing/src/rsa_local/rsa_local.go b/post-quantum-server-testing/src/rsa_local/rsa_local.go index 53e85a8..1116515 100644 --- a/post-quantum-server-testing/src/rsa_local/rsa_local.go +++ b/post-quantum-server-testing/src/rsa_local/rsa_local.go @@ -13,7 +13,7 @@ import ( ) const ( - KeySize = 4096 + KEYSIZE = 4096 ) // Public key struct @@ -35,7 +35,7 @@ type response struct { // GenerateKey generates a new RSA-4096 key pair func GenerateKey() (*PrivateKey, error) { - sk, err := rsa.GenerateKey(rand.Reader, KeySize) + sk, err := rsa.GenerateKey(rand.Reader, KEYSIZE) if err != nil { return nil, err } From d7101b18a7836b18eefd05c5e51c23d3e5655535 Mon Sep 17 00:00:00 2001 From: Haseeb Afzal Date: Tue, 3 Feb 2026 10:43:55 -0500 Subject: [PATCH 19/19] switched rsa to kms and ecdsa to local --- .../ecdsa_local.go} | 31 ++++++++-------- post-quantum-server-testing/src/main.go | 36 +++++++++---------- .../src/{ecdsa/ecdsa.go => rsa/rsa.go} | 10 +++--- 3 files changed, 38 insertions(+), 39 deletions(-) rename post-quantum-server-testing/src/{rsa_local/rsa_local.go => ecdsa_local/ecdsa_local.go} (71%) rename post-quantum-server-testing/src/{ecdsa/ecdsa.go => rsa/rsa.go} (96%) diff --git a/post-quantum-server-testing/src/rsa_local/rsa_local.go b/post-quantum-server-testing/src/ecdsa_local/ecdsa_local.go similarity index 71% rename from post-quantum-server-testing/src/rsa_local/rsa_local.go rename to post-quantum-server-testing/src/ecdsa_local/ecdsa_local.go index 1116515..4db24bb 100644 --- a/post-quantum-server-testing/src/rsa_local/rsa_local.go +++ b/post-quantum-server-testing/src/ecdsa_local/ecdsa_local.go @@ -1,10 +1,10 @@ -package rsa_local +package ecdsa_local import ( - "crypto" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" - "crypto/rsa" - "crypto/sha256" + "crypto/sha512" "crypto/x509" "encoding/base64" "fmt" @@ -12,19 +12,15 @@ import ( "sync" ) -const ( - KEYSIZE = 4096 -) - // Public key struct type PublicKey struct { - Pk *rsa.PublicKey + Pk *ecdsa.PublicKey } // Private key struct type PrivateKey struct { PublicKey - Sk *rsa.PrivateKey + Sk *ecdsa.PrivateKey } // Response struct @@ -33,9 +29,9 @@ type response struct { PublicKey string `json:"publicKey"` } -// GenerateKey generates a new RSA-4096 key pair +// GenerateKey generates a new ECDSA P-384 key pair func GenerateKey() (*PrivateKey, error) { - sk, err := rsa.GenerateKey(rand.Reader, KEYSIZE) + sk, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) if err != nil { return nil, err } @@ -48,10 +44,10 @@ func GenerateKey() (*PrivateKey, error) { return privateKey, nil } -// SignPQC signs a message using the private key +// signPQC signs a message using the private key func (priv *PrivateKey) signPQC(msg []byte) ([]byte, error) { - hash := sha256.Sum256(msg) - sign, err := rsa.SignPSS(rand.Reader, priv.Sk, crypto.SHA256, hash[:], nil) + hash := sha512.Sum384(msg) + sign, err := ecdsa.SignASN1(rand.Reader, priv.Sk, hash[:]) if err != nil { return nil, fmt.Errorf("signing failed: %v", err) } @@ -72,7 +68,10 @@ func SignData(input string, privKey *PrivateKey, wg *sync.WaitGroup) response { log.Fatalf("error signing message: %v", err) } - pubKey := x509.MarshalPKCS1PublicKey(privKey.PublicKey.Pk) + pubKey, err := x509.MarshalPKIXPublicKey(privKey.PublicKey.Pk) + if err != nil { + log.Fatalf("error marshaling public key: %v", err) + } resp := response{ Signature: base64.StdEncoding.EncodeToString(signature), PublicKey: base64.StdEncoding.EncodeToString(pubKey), diff --git a/post-quantum-server-testing/src/main.go b/post-quantum-server-testing/src/main.go index c2b4888..669f826 100644 --- a/post-quantum-server-testing/src/main.go +++ b/post-quantum-server-testing/src/main.go @@ -4,10 +4,10 @@ import ( "fmt" "log" "os" - "server-testing/ecdsa" + "server-testing/ecdsa_local" "server-testing/falcon" "server-testing/mldsa" - "server-testing/rsa_local" + "server-testing/rsa" "strings" "sync" "time" @@ -112,8 +112,7 @@ func testFalconMediumPayload(iterations int, privKey *falcon.PrivateKey) { fmt.Printf("Medium Payload Falcon-512: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testEcdsaSmallPayload(iterations int, location string, keyRing string, projectID string) { - keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) +func testEcdsaSmallPayload(iterations int, privKey *ecdsa_local.PrivateKey) { // Get the time pre-signing start := time.Now() @@ -123,7 +122,7 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj for i := 0; i < iterations; i++ { go func(j int) { - _ = ecdsa.SignData(SMALLINPUT, "ecdsa", keyName, &wg) + _ = ecdsa_local.SignData(SMALLINPUT, privKey, &wg) }(i) } wg.Wait() @@ -133,8 +132,7 @@ func testEcdsaSmallPayload(iterations int, location string, keyRing string, proj fmt.Printf("Small Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testEcdsaMediumPayload(iterations int, location string, keyRing string, projectID string) { - keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/ecdsa/cryptoKeyVersions/1", projectID, location, keyRing) +func testEcdsaMediumPayload(iterations int, privKey *ecdsa_local.PrivateKey) { // Get the time pre-signing start := time.Now() @@ -144,7 +142,7 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro for i := 0; i < iterations; i++ { go func(j int) { - _ = ecdsa.SignData(MEDIUMINPUT, "ecdsa", keyName, &wg) + _ = ecdsa_local.SignData(MEDIUMINPUT, privKey, &wg) }(i) } wg.Wait() @@ -154,7 +152,8 @@ func testEcdsaMediumPayload(iterations int, location string, keyRing string, pro fmt.Printf("Medium Payload ECDSA-384: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testRsaSmallPayload(iterations int, privKey *rsa_local.PrivateKey) { +func testRsaSmallPayload(iterations int, location string, keyRing string, projectID string) { + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/rsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() @@ -164,7 +163,7 @@ func testRsaSmallPayload(iterations int, privKey *rsa_local.PrivateKey) { for i := 0; i < iterations; i++ { go func(j int) { - _ = rsa_local.SignData(SMALLINPUT, privKey, &wg) + _ = rsa.SignData(SMALLINPUT, "rsa", keyName, &wg) }(i) } wg.Wait() @@ -174,7 +173,8 @@ func testRsaSmallPayload(iterations int, privKey *rsa_local.PrivateKey) { fmt.Printf("Small Payload RSA-4096: %.3f ms\n", (elapsed.Seconds()*1000)/float64(iterations)) } -func testRsaMediumPayload(iterations int, privKey *rsa_local.PrivateKey) { +func testRsaMediumPayload(iterations int, location string, keyRing string, projectID string) { + keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/rsa/cryptoKeyVersions/1", projectID, location, keyRing) // Get the time pre-signing start := time.Now() @@ -184,7 +184,7 @@ func testRsaMediumPayload(iterations int, privKey *rsa_local.PrivateKey) { for i := 0; i < iterations; i++ { go func(j int) { - _ = rsa_local.SignData(MEDIUMINPUT, privKey, &wg) + _ = rsa.SignData(MEDIUMINPUT, "rsa", keyName, &wg) }(i) } wg.Wait() @@ -206,8 +206,8 @@ func main() { log.Fatalf("Failed to generate a private key: %v", err) } - // Generate a rsa private key - privKeyRsa, err := rsa_local.GenerateKey() + // Generate an ecdsa private key + privKeyEcdsa, err := ecdsa_local.GenerateKey() if err != nil { log.Fatalf("Failed to generate a private key: %v", err) } @@ -216,14 +216,14 @@ func main() { // Run the small payload tests testFalconSmallPayload(iterations, privKeyFalcon) - testRsaSmallPayload(iterations, privKeyRsa) + testEcdsaSmallPayload(iterations, privKeyEcdsa) + testRsaSmallPayload(iterations, location, keyRing, projectID) testMldsaSmallPayload(iterations, location, keyRing, projectID) - testEcdsaSmallPayload(iterations, location, keyRing, projectID) // Run the medium payload tests testFalconMediumPayload(iterations, privKeyFalcon) - testRsaMediumPayload(iterations, privKeyRsa) + testEcdsaMediumPayload(iterations, privKeyEcdsa) + testRsaMediumPayload(iterations, location, keyRing, projectID) testMldsaMediumPayload(iterations, location, keyRing, projectID) - testEcdsaMediumPayload(iterations, location, keyRing, projectID) } diff --git a/post-quantum-server-testing/src/ecdsa/ecdsa.go b/post-quantum-server-testing/src/rsa/rsa.go similarity index 96% rename from post-quantum-server-testing/src/ecdsa/ecdsa.go rename to post-quantum-server-testing/src/rsa/rsa.go index e5b4029..5e46c0a 100644 --- a/post-quantum-server-testing/src/ecdsa/ecdsa.go +++ b/post-quantum-server-testing/src/rsa/rsa.go @@ -1,8 +1,8 @@ -package ecdsa +package rsa import ( "context" - "crypto/sha512" + "crypto/sha256" "encoding/base64" "fmt" "hash/crc32" @@ -53,7 +53,7 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { plaintext := message // Calculate the digest of the message. - digest := sha512.New384() + digest := sha256.New() if _, err := digest.Write(plaintext); err != nil { return nil, fmt.Errorf("failed to create digest: %w", err) } @@ -70,8 +70,8 @@ func signAsymmetric(name string, message []byte) ([]byte, error) { req := &kmspb.AsymmetricSignRequest{ Name: name, Digest: &kmspb.Digest{ - Digest: &kmspb.Digest_Sha384{ - Sha384: digest.Sum(nil), + Digest: &kmspb.Digest_Sha256{ + Sha256: digest.Sum(nil), }, }, DigestCrc32C: wrapperspb.Int64(int64(digestCRC32C)),