github.com/google/osv-scalibr@v0.4.1/veles/secrets/privatekey/detector_test.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package privatekey_test 16 17 import ( 18 "crypto/ecdsa" 19 "crypto/elliptic" 20 "crypto/rand" 21 "crypto/rsa" 22 "crypto/x509" 23 "testing" 24 25 "github.com/google/osv-scalibr/veles/secrets/privatekey" 26 ) 27 28 func TestDetector_PEMVariants(t *testing.T) { 29 d := privatekey.NewDetector() 30 31 cases := []struct { 32 name, data string 33 }{ 34 {"Generic", `-----BEGIN PRIVATE KEY----- 35 MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDwxXFmUGrIkQZ 36 AJ1yFkkXrOOCH3RL4QsGk24xTj7iO/ozauKXoVys3wKAzK9iwlfBY36kqqn0hvL9 37 YhrnHAGfQJTGMnwkbZvWV6u6IKWxKqPGQp4ULIbveYd6FoXPMcwULdoRQeR+kez8 38 aPzyu/L4cnlk31J6WqpNnjohmndAkaRnx/mGRYcn+xDvOcJbmLSHQHYgCigHzENg 39 uilu4N9KLsD6+N0c1prr79STsiR9HRozkS+ySpiS1tG9jYMAlKSp3L+hgQDqvcK5 40 j5zyJx7iDmIdmQjmbpnLaxj8cjW+c5FzEoqZ2pXCuLDMTOfGnzFxrRzf5Mc8tw+9 41 hPRrkv+fAgMBAAECggEAGn6+5Z2We7kJinDA3n4Rqnil2iizrslomp09nsK+VBRW 42 Crt+q5MVXfhY+GG7oxw2kGAM9fB7TDMvlAfBKGJr/cfZ2vFeR/flzZ7UCT797fqd 43 a+n8RzK3mJXUNjvyJFbTDjAegZNvf4n0jz0ObzPs8J9dur9XBGRdBGBT8dRcK4rN 44 +F9qh8JYwM+cXYbDjKvoLoxTSeCxREJ2KHVueCGxTBAwkmUXiF0jnxueqLoThoAT 45 TLzyTYw+20F4vRJMVpLZO6X7GOht1NkIbi4vFTKh8iAnUGXRUZ8W+evW2NykGd0q 46 0QsDsFO/Oc8Xn2DZTCutGUsHeDxq4XApnNk04t13sQKBgQDw+79+6E9xXjYUwZrG 47 TyMb1j1va/oGEVaxPgSGm5RHcjW4xesdCftOT3eckFNVWzk4V7sG9as/s0FMQ0M3 48 TchY8FkeK/iOBbZDExmmeDPvxzexC7nRCB+NOJZML9zCN8PTkje96uXWjTCCbJTu 49 zqnliDLTBsXJQt1XJXU5ZnIQxQKBgQDP9fNEyRzNXttKL7lj9zutDe5AMHS10hoR 50 gMBnRKlte3VKZfRauna/Lv3afhFHwZAEWnkUhQUK9lE8U9EnIoPNPW7jAaO69BHo 51 1/gXR9rZibiTnYczCS9XlXaER3139Mjjn3W0v12Vi9Fylqgx2oyb1HaDtRr6HNun 52 P33B8dbNEwKBgQDoOnLMJauJILUVQ42X1eOLi+YgXfnPpx3YKF/MKFm4kENdEL4G 53 efwH92TZJ+xmsUZvGXxOtKiW9nPSvm8j+H092EDJZq5cjvyZnup1FhlW1LDCmP40 54 hpOBUCrmuKkRMRQx6xJ0ns1m+SDqTyEnEVmArMPtwPURgrIyrRJOgn8h0QKBgAK4 55 K6M1ogvJdsKklx8Ih54+tWPfflc2VSLvdRSkoDaPS7xaUvSwxYbAfY9S4LT4ggKc 56 kELFbohzKiLI0c5aNDEF4aJUTijOskFCObtMND9t/pznjXIMZ7MUgEVAjhJ4f/wC 57 BM8FRZsEBgwijjaAriAHijk0sBKfN/wa53EW0YFDAoGAE+k5Eq/L+/G4pDcOwm55 58 kEzYclnfD38ZM9DfPB6k0K55TubLL9PeltRkR5yy4tjlBlDPx6wzMhbd0Xq3iCzq 59 Twanj5YBWrq2yV2fqWgvyz3LIqlhmDNW89ThWmk7XYtD0em9dnEXlpH0JTxdQpCF 60 tGOp/d/V3F66yalNSTXNbkA= 61 -----END PRIVATE KEY----- 62 `}, 63 {"EC", `-----BEGIN EC PRIVATE KEY----- 64 MHcCAQEEIJzFi+JZBWu0ivAZgkOuKD8OMs69xpRj7SqmVlqSbqb7oAoGCCqGSM49 65 AwEHoUQDQgAE8cg/mxOyT54gVISe5vMWP//1lUcElOs5T2SNJ+PbCrzt6SrwRiM3 66 Um1TNu0yGY4QbHWoW9iq8ZSVN7SSrcAong== 67 -----END EC PRIVATE KEY----- 68 `}, 69 {"OpenSSH", `-----BEGIN OPENSSH PRIVATE KEY----- 70 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn 71 NhAAAAAwEAAQAAAIEA2QxOpGlLR/cvp54L6jwu+edKTVdPFs9K3w17oI1w5DhWTEce6nh7 72 nCv+e3luyWbxuxnpyEiJiqTP7FjdX/lzKMHTvhe1PTP5nIIQhKQCunE4x9k5NrK1BZBKGo 73 Ypg343xdPtGLNRdcaAIppuWhbmWCpgciYkAgcae7qP8ZujVCcAAAIIQp//80Kf//MAAAAH 74 c3NoLXJzYQAAAIEA2QxOpGlLR/cvp54L6jwu+edKTVdPFs9K3w17oI1w5DhWTEce6nh7nC 75 v+e3luyWbxuxnpyEiJiqTP7FjdX/lzKMHTvhe1PTP5nIIQhKQCunE4x9k5NrK1BZBKGoYp 76 g343xdPtGLNRdcaAIppuWhbmWCpgciYkAgcae7qP8ZujVCcAAAADAQABAAAAgQDKSYYiBW 77 B8OgzYE5zXOjAuCTpeyriTca8+I7rM8AX/LeKAROizbocGDpqnSY3Pd3pj/sq8N5648NI4 78 XLo18K853cdGKdpbZHxJ//1CSp4s2YEbzJH2AYVITDv2Lc/f3Ze2Ra93y4RQDmz310R3+T 79 9vQNi0F22XEb7kXa75ombdAQAAAEAmqs7ILluwoEzCIwJvl8qBAqAfGnDbTC1uFsUwlskX 80 Ay6Ku8/HplhGvnl0fL51B7BC38qXopDRYU0avvACpnOcAAAAQQDtC80p4AE+fVW7Ltngca 81 8K1TrL/GWkZSnuruXPiXiZFyQYoqiXyllLekjpBDxQSiKbGWa7t7AScAyso6EBbZvRAAAA 82 QQDqZyhZXF7vc5Com3IzHkY2apHsWq5N8A5BQd9Wzh9KsOTvrH1W27m5TNcX7joVpHSYMl 83 YE2rTfFMPD137muQZ3AAAAEnlzYXhlbmF4QGdtYWlsLmNvbQ== 84 -----END OPENSSH PRIVATE KEY----- 85 `}, 86 } 87 88 for _, tc := range cases { 89 t.Run(tc.name, func(t *testing.T) { 90 secrets, offsets := d.Detect([]byte(tc.data)) 91 if len(secrets) != 1 { 92 t.Fatalf("expected 1 match, got %d (offsets: %v)", len(secrets), offsets) 93 } 94 }) 95 } 96 } 97 98 func TestDetector_DERVariants(t *testing.T) { 99 d := privatekey.NewDetector() 100 t.Run("PKCS1", func(t *testing.T) { 101 key, _ := rsa.GenerateKey(rand.Reader, 2048) 102 der := x509.MarshalPKCS1PrivateKey(key) 103 104 secrets, offsets := d.Detect(der) 105 if len(secrets) != 1 { 106 t.Fatalf("expected 1 matches, got %d (offsets: %v)", len(secrets), offsets) 107 } 108 }) 109 110 t.Run("PKCS8", func(t *testing.T) { 111 key, _ := rsa.GenerateKey(rand.Reader, 2048) 112 der, _ := x509.MarshalPKCS8PrivateKey(key) 113 114 secrets, offsets := d.Detect(der) 115 if len(secrets) != 1 { 116 t.Fatalf("expected 1 matches, got %d (offsets: %v)", len(secrets), offsets) 117 } 118 }) 119 120 t.Run("EC", func(t *testing.T) { 121 key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 122 der, _ := x509.MarshalECPrivateKey(key) 123 124 secrets, offsets := d.Detect(der) 125 if len(secrets) != 1 { 126 t.Fatalf("expected 1 matches, got %d (offsets: %v)", len(secrets), offsets) 127 } 128 }) 129 } 130 131 func TestDetector_MultipleBlocksIndependent(t *testing.T) { 132 d := privatekey.NewDetector() 133 data := `-----BEGIN EC PRIVATE KEY----- 134 MHcCAQEEIJzFi+JZBWu0ivAZgkOuKD8OMs69xpRj7SqmVlqSbqb7oAoGCCqGSM49 135 AwEHoUQDQgAE8cg/mxOyT54gVISe5vMWP//1lUcElOs5T2SNJ+PbCrzt6SrwRiM3 136 Um1TNu0yGY4QbHWoW9iq8ZSVN7SSrcAong== 137 -----END EC PRIVATE KEY----- 138 ` + 139 "SOMEDATA\n" + 140 `-----BEGIN EC PRIVATE KEY----- 141 MHcCAQEEIJzFi+JZBWu0ivAZgkOuKD8OMs69xpRj7SqmVlqSbqb7oAoGCCqGSM49 142 AwEHoUQDQgAE8cg/mxOyT54gVISe5vMWP//1lUcElOs5T2SNJ+PbCrzt6SrwRiM3 143 Um1TNu0yGY4QbHWoW9iq8ZSVN7SSrcAong== 144 -----END EC PRIVATE KEY----- 145 ` 146 secrets, offsets := d.Detect([]byte(data)) 147 148 if len(secrets) != 2 { 149 t.Fatalf("expected 2 matches, got %d (offsets: %v)", len(secrets), offsets) 150 } 151 } 152 153 func TestDetector_InvalidInputs(t *testing.T) { 154 d := privatekey.NewDetector() 155 156 cases := []struct { 157 name string 158 data []byte 159 }{ 160 { 161 name: "CorruptedPEM", 162 data: []byte("-----BEGIN PRIVATE KEY-----\nINVALIDDATA\n-----END PRIVATE KEY-----"), 163 }, 164 { 165 name: "MissingDashes", 166 data: []byte("BEGIN PRIVATE KEY (missing dashes) END PRIVATE KEY"), 167 }, 168 { 169 name: "RandomString", 170 data: []byte("just some random text that looks nothing like a key"), 171 }, 172 { 173 name: "InvalidDER", 174 data: []byte("notaderkey"), 175 }, 176 } 177 178 for _, tc := range cases { 179 t.Run(tc.name, func(t *testing.T) { 180 secrets, offsets := d.Detect(tc.data) 181 if len(secrets) != 0 { 182 t.Fatalf("expected 0 matches, got %d (offsets: %v)", len(secrets), offsets) 183 } 184 }) 185 } 186 }