Files
releases/bundle/verify_test.go
T
Johan Fylling 5464b005e8 Bumping golangci-lint to v1.59.1 (#6817)
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
2024-06-19 15:13:43 +02:00

322 lines
17 KiB
Go

// Copyright 2020 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package bundle
import (
"bytes"
"errors"
"testing"
)
func TestVerifyBundleSignature(t *testing.T) {
badToken := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.4nrInalqppAc9EjsNUD9Y35amVpDGoRk4bkxzdY8fhs`
signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs`
otherSignedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTMvcm9sZXMvZGF0YS5qc29uIiwiaGFzaCI6ImMyMTMxNTQ0YzcxNmEyNWE1ZTMxZjUwNDMwMGY1MjQwZTgyMzVjYWRiOWE1N2YwYmQxYjZmNGJkNzRiMjY2MTIiLCJhbGdvcml0aG0iOiJtZDUifSx7Im5hbWUiOiJkYi91YW0zL3BvbGljeS9wb2xpY3kucmVnbyIsImhhc2giOiI0MmNmZTY3NjhiNTdiYjVmNzUwM2MxNjVjMjhkZDA3YWM1YjgxMzU1NGViYzg1MGYyY2MzNTg0M2U3MTM3YjFkIn0seyJuYW1lIjoiZGIvdWFtNC9wb2xpY3kvcmVnby5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQiLCJhbGdvcml0aG0iOiJzaGEzODQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsImtleWlkIjoiYmFyIiwic2NvcGUiOiJ3cml0ZSJ9.d_NiBXF3zqNPZCEubQC1FC1IYwmwkYwjv00B5UyJ9Dk`
badTokenHeaderBase64 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvby 9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs`
badTokenHeaderJSON := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyX9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs`
badTokenPayload := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6eyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifSwiaWF0IjoxNTkyMjQ4MDI3LCJpc3MiOiJKV1RTZXJ2aWNlIiwia2V5aWQiOiJmb28iLCJzY29wZSI6IndyaXRlIn0.J3KJFOycHPy4Wkw_LzzIKvTMqCsV8L8DdQW5Q-vieKg`
tests := map[string]struct {
input SignaturesConfig
readerVerifyConfig *VerificationConfig
wantErr bool
err error
}{
"no_signatures": {SignaturesConfig{}, nil, true, errors.New(".signatures.json: missing JWT (expected exactly one)")},
"multiple_signatures": {SignaturesConfig{Signatures: []string{signedTokenHS256, otherSignedTokenHS256}}, nil, true, errors.New(".signatures.json: multiple JWTs not supported (expected exactly one)")},
"invalid_token": {SignaturesConfig{Signatures: []string{badToken}}, nil, true, errors.New("failed to split compact serialization")},
"invalid_token_header_base64": {
SignaturesConfig{Signatures: []string{badTokenHeaderBase64}},
NewVerificationConfig(nil, "", "", nil),
true, errors.New("failed to base64 decode JWT headers: illegal base64 data at input byte 50"),
},
"invalid_token_header_json": {
SignaturesConfig{Signatures: []string{badTokenHeaderJSON}},
NewVerificationConfig(nil, "", "", nil),
true, errors.New("failed to parse JWT headers: unexpected end of JSON input"),
},
"bad_token_payload": {SignaturesConfig{Signatures: []string{badTokenPayload}}, nil, true, errors.New("json: cannot unmarshal object into Go struct field DecodedSignature.files of type []bundle.FileInfo")},
"valid_token_and_scope": {
SignaturesConfig{Signatures: []string{signedTokenHS256}},
NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil),
false, nil,
},
"valid_token_and_scope_mismatch": {
SignaturesConfig{Signatures: []string{signedTokenHS256}},
NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "bad_scope", nil),
true, errors.New("scope mismatch"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
_, err := VerifyBundleSignature(tc.input, tc.readerVerifyConfig)
if tc.wantErr {
if err == nil {
t.Fatal("Expected error but got nil")
}
if tc.err != nil && tc.err.Error() != err.Error() {
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
}
} else {
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
}
})
}
// verify the number files on the reader collected from the JWT
sc := SignaturesConfig{Signatures: []string{signedTokenHS256}}
verificationConfig := NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil)
files, err := VerifyBundleSignature(sc, verificationConfig)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
expectedNumFiles := 2
if len(files) != expectedNumFiles {
t.Fatalf("Expected %v files in the JWT payloads but got %v", expectedNumFiles, len(files))
}
}
func TestVerifyJWTSignature(t *testing.T) {
signedNoKeyIDTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.N2DaEyhfACMAij2sRC0ZDwNz4wI7BX_flH3IkKqMvE4`
signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs`
signedTokenWithDeprecatedKidClaimHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsImtleWlkIjoiZm9vIiwic2NvcGUiOiJ3cml0ZSJ9.4nrInalqppAc9EjsNUD9Y35amVpDGoRk4bkxzdY8fhs`
signedTokenWithBarKidHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJhciJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.xW04tph2-dSy47uZ-CgiObvFWFLUsMym8N7ermgPZ0s`
signedTokenRS256 := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI1MTQwNiwiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.UZcX_Qj29o213nwZ5mysiyNcgAp1AOqCkDLkj1KGZ7Cw3l17TbkF8t3FJd654sf762NGcABA0WQ2zxq-labXl_d5TKQmq23lurO3qLHI1cKQPyoxPbddGo78sJpMwWsiMyEYcyyKC9FpNUpa8cT81GPo4q9Zui8TAD0wtHh2YxBL7E8QYLJxcLMJTJxVBgrQGLXFT7vYRc6hJz9Vu-i0SWuLBGxOCmYoHr4DWFfy0IhAbhiUFyLRpukfBPjCJNYY5LgaZdN6LaYivtIEUms5jUuXubBimLJm7KOT5-bgJMXTcUsbrbl2Ma7PwG5IOKjjgyEvH8KVfGFZSzNrpuxKmA`
publicKeyValid := `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9KaakMv1XKKDaSch3PFR
3a27oaHp1GNTTNqvb1ZaHZXp+wuhYDwc/MTE67x9GCifvQBWzEGorgTq7aisiOyl
vKifwz6/wQ+62WHKG/sqKn2Xikp3P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+m
z5SiKGBwviSY0Th2L4e5sGJuk2HOut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+l
GeR6HUOXqoYVf7ff126GP7dticTVBgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7P
ypZ/TdlnuGC1cOpAVyU7k32IJ9CRbt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0
+wIDAQAB
-----END PUBLIC KEY-----`
publicKeyInvalid := `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMYfnvWtC8Id5bPKae5yXSxQTt
+Zpul6AnnZWfI2TtIarvjHBFUtXRo96y7hoL4VWOPKGCsRqMFDkrbeUjRrx8iL91
4/srnyf6sh9c8Zk04xEOpK1ypvBz+Ks4uZObtjnnitf0NBGdjMKxveTq+VE7BWUI
yQjtQ8mbDOsiLLvh7wIDAQAB
-----END PUBLIC KEY-----`
publicKeyBad := `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMYfnvWtC8Id5bPKae5yXSxQTt
+Zpul6AnnZWfI2TtIarvjHBFUtXRo96y7hoL4VWOPKGCsRqMFDkrbeUjRrx8iL91
4/srnyf6sh9c8Zk04xEOpK1ypvBz+Ks4uZObtjnnitf0NBGdjMKxveTq+VE7BWUI
yQjtQ8mbDOsiLLvh7wIDAQAB==
-----END PUBLIC KEY-----`
// Private key corresponding to the valid public key, for
// re-generating the test signatures.
/* privateKey := `-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA9KaakMv1XKKDaSch3PFR3a27oaHp1GNTTNqvb1ZaHZXp+wuh
YDwc/MTE67x9GCifvQBWzEGorgTq7aisiOylvKifwz6/wQ+62WHKG/sqKn2Xikp3
P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+mz5SiKGBwviSY0Th2L4e5sGJuk2HO
ut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+lGeR6HUOXqoYVf7ff126GP7dticTV
BgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7PypZ/TdlnuGC1cOpAVyU7k32IJ9CR
bt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0+wIDAQABAoIBAQDDL0BVkUNZ+pYZ
CI1ttmH4GCmAFKt3NR86S6Z3j08qF3arQWYoXw1JZLsu0ByFb7OxmFmncCLhYy8D
GPU98H9x+p4rqR5XKvOJhwk2NbY4XCbQwARPm6Y6v/f+rSE/U+l9EXrHsrrrZNln
JWtABpwRNKYCzJ5mNNBu6zrvRLuSygXG/U5wMnO7bLAJh7u3SUPd6Mkg+RTEu4jG
wHuhcnbsbBdpLRHZKeGlfVL62sUXE1mrf09406U2i7us9BhvYC6pEgbVi7PZ0FBs
Lx0W1PB0GdKS0hq6yKQhskBFI/CsGHoIjIsG7AiUyGwxLgeVHyOfUTZGaVRVZ99j
+nz6rOhRAoGBAP6nW/oDaaVZM0JiO09KdbEmtUeNqKPGM1rNtakbiBz5JZ6M636L
2Q///7jvyIrI6WNRTQh2ByqKpxwHjd0+mJa1CiOrTVE44mcxaXoSdQWtXJhZt24O
kOwVWgy5P/nyIVq+l+vYFJvMq387c9h/l2cFaSa5fIiTbTFq3ue8Mmp1AoGBAPXx
tPCHeGa8WwKvMwVB4hZ5XogTRLqtTIftqhrJvSp/2md60Xz7A35HuOTV22PFuoUt
pIaJggPVHOd7tsSYEx2wjJpTNf+EWhIdp7kziHIqGgjj/d+NftRDvx3e/mJip4TI
XEN0BzxCplU/eFPL8bvi03fjnCOh2iG599d8vtOvAoGBAMlB1ZRDLDSMydE2N2+U
Bm3qjKyvTU+aLi4ek+rBopJbahrjfp61weg+R4mOoGznGmTu9TWxqjo5+JZTdhAc
D5ZUIF5OXT3K+kvaJmVevvOsrpiNl0W451peCZwysFhGv4urRAAV9zumxwc4IndB
Z5P5F8COKdj6wvqiXubAuwudAoGAD6gPaLB3DbM35/fXO6JyDhQz3F29plSZ5p1O
kt382NPCx4ueAmLIWiWes5KZoMRZl1jMfHQMfsn2SRYrEGDN9rnieYCKk3WNdlHE
95k8OmhLt/0rkCulw0V8yR4E+6ZkG6PVm8WrID7t78dWlZ8KCHfsFlm6+tm21SbN
jD44t6kCgYEAnTHH8SOMoW/kMckESubV2y4rvdLol7nVX3ia7IGP8TA6NHsGT0Qb
EOuKwnuQ3ZToMdNVrS2nFyLe/HohSbT0SrNu2j4YkZDxHGQImZvw/KtMFPyT+Ff8
rtvSvuiJNjLUr6OcqxARXrmispBO/GxvWucF0tcpOcSGUDYHAD2yIAQ=
-----END RSA PRIVATE KEY-----`*/
tests := map[string]struct {
token string
keys map[string]*KeyConfig
keyID string
scope string
wantErr bool
err error
}{
"no_public_key_id": {signedNoKeyIDTokenHS256, map[string]*KeyConfig{}, "", "", true, errors.New("verification key ID is empty")},
"actual_public_key_missing": {signedTokenHS256, map[string]*KeyConfig{}, "", "", true, errors.New("verification key corresponding to ID foo not found")},
"deprecated_key_id_claim": {
signedTokenWithDeprecatedKidClaimHS256,
map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", // check valid keyId in deprecated claim is used
false, nil,
},
"bad_public_key_algorithm": {
signedTokenHS256,
map[string]*KeyConfig{"foo": {Key: "somekey", Algorithm: "RS007"}}, "", "",
true, errors.New("unsupported signature algorithm: RS007"),
},
"public_key_with_valid_HS256_sign": {
signedTokenWithBarKidHS256,
map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "write", // check keyId in OPA config takes precedence
false, nil,
},
"public_key_with_invalid_HS256_sign": {
signedTokenHS256,
map[string]*KeyConfig{"foo": {Key: "bad_secret", Algorithm: "HS256"}}, "", "",
true, errors.New("failed to verify message: failed to match hmac signature"),
},
"public_key_with_valid_RS256_sign": {
signedTokenRS256,
map[string]*KeyConfig{"foo": {Key: publicKeyValid, Algorithm: "RS256"}}, "", "write",
false, nil,
},
"public_key_with_invalid_RS256_sign": {
signedTokenRS256,
map[string]*KeyConfig{"foo": {Key: publicKeyInvalid, Algorithm: "RS256"}}, "", "",
true, errors.New("failed to verify message: crypto/rsa: verification error"),
},
"public_key_with_bad_cert_RS256": {
signedTokenRS256,
map[string]*KeyConfig{"foo": {Key: publicKeyBad, Algorithm: "RS256"}}, "foo", "",
true, errors.New("failed to parse PEM block containing the key"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
_, err := verifyJWTSignature(tc.token, NewVerificationConfig(tc.keys, tc.keyID, tc.scope, nil))
if tc.wantErr {
if err == nil {
t.Fatal("Expected error but got nil")
}
if tc.err != nil && tc.err.Error() != err.Error() {
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
}
} else {
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
}
})
}
// public key id provided in OPA config, actual public key signed using RS256. Valid signature
keys := map[string]*KeyConfig{}
keys["foo"] = &KeyConfig{
Key: publicKeyValid,
Algorithm: "RS256",
}
_, err := verifyJWTSignature(signedTokenRS256, NewVerificationConfig(keys, "foo", "write", nil))
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
}
func TestVerifyBundleFile(t *testing.T) {
tests := map[string]struct {
files [][2]string
readerFiles map[string]FileInfo
wantErr bool
err error
}{
"file_not_found": {
[][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}},
map[string]FileInfo{},
true, errors.New("file /.manifest not included in bundle signature"),
},
"bad_hashing_algorithm": {
[][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}},
map[string]FileInfo{"/.manifest": {
Name: "/.manifest",
Hash: "e7dc95e14ad6cd75d044c13d52ee3ab1",
Algorithm: "MD6",
}},
true, errors.New("unsupported hashing algorithm: MD6"),
},
"bad_digest": {
[][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}},
map[string]FileInfo{"/.manifest": {
Name: "/.manifest",
Hash: "874984d68515ba2439c04dddf5b21574",
Algorithm: MD5.String(),
}},
true, errors.New("/.manifest: digest mismatch (want: 874984d68515ba2439c04dddf5b21574, got: a005c38a509dc2d5a7407b9494efb2ad)"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
data := bytes.NewBufferString(tc.files[0][1])
err := VerifyBundleFile(tc.files[0][0], *data, tc.readerFiles)
if tc.wantErr {
if err == nil {
t.Fatal("Expected error but got nil")
}
if tc.err != nil && tc.err.Error() != err.Error() {
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
}
} else {
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
}
})
}
}
type CustomVerifier struct{}
func (*CustomVerifier) VerifyBundleSignature(_ SignaturesConfig, _ *VerificationConfig) (map[string]FileInfo, error) {
return map[string]FileInfo{}, nil
}
func TestCustomVerifier(t *testing.T) {
custom := &CustomVerifier{}
err := RegisterVerifier(defaultVerifierID, custom)
if err == nil {
t.Fatalf("Expected error when registering with default ID")
}
if err := RegisterVerifier("_test", custom); err != nil {
t.Fatal(err)
}
defaultVerifier, err := GetVerifier(defaultVerifierID)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if _, isDefault := defaultVerifier.(*DefaultVerifier); !isDefault {
t.Fatalf("Expected DefaultVerifier to be registered at key %s", defaultVerifierID)
}
customVerifier, err := GetVerifier("_test")
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if _, isCustom := customVerifier.(*CustomVerifier); !isCustom {
t.Fatalf("Expected CustomVerifier to be registered at key _test")
}
if _, err = GetVerifier("_unregistered"); err == nil {
t.Fatalf("Expected error when no Verifier exists at provided key")
}
}