mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-27 02:34:52 -06:00
338583c18a
These changes add support for digital signatures for policy bundles which can be used to verify their authenticity. Bundle signature verification involves the following steps: * Verify the JWT signature * Verify the files in the JWT payload exist in the bundle * Verify the file content of the files in bundle match with those in the payload This commit adds a new `sign` command to generate a digital signature for policy bundles. For more details, run "opa sign --help" The signatures generated by the 'sign' command can be verified by the 'build' command. The 'build' command can also sign the bundle it generates. The 'run' command can verify a signed bundle or skip verification altogether. OPA 'sign', 'build' and 'run' can be used to sign/verify bundles in bundle mode (--bundle) mode only. Verification can be also be performed when bundle downloading is enabled. Fixes: #1757 Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
124 lines
2.6 KiB
Go
124 lines
2.6 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"
|
||
"encoding/json"
|
||
"testing"
|
||
)
|
||
|
||
func TestHashFile(t *testing.T) {
|
||
|
||
mapInput := map[string]interface{}{
|
||
"key1": []interface{}{
|
||
"element1",
|
||
"element2",
|
||
},
|
||
"key2": map[string]interface{}{
|
||
"a": 0,
|
||
"b": 1,
|
||
"c": json.Number("123.45678911111111111111111111111111111111111111111111111"),
|
||
},
|
||
}
|
||
|
||
arrayInput := []interface{}{
|
||
[]string{"foo", "bar"},
|
||
mapInput,
|
||
`package example`,
|
||
[]string{"$", "α", "©", "™"},
|
||
}
|
||
|
||
tests := map[string]struct {
|
||
input interface{}
|
||
algorithm HashingAlgorithm
|
||
}{
|
||
"map": {mapInput, SHA256},
|
||
"array": {arrayInput, MD5},
|
||
"string": {"abc", SHA256},
|
||
"string_with_html_chars": {"<foo></foo>", SHA256},
|
||
"null": {`null`, SHA512},
|
||
"bool": {false, SHA256},
|
||
}
|
||
|
||
for name, tc := range tests {
|
||
t.Run(name, func(t *testing.T) {
|
||
|
||
h, _ := NewSignatureHasher(tc.algorithm)
|
||
|
||
// compute hash from the raw bytes
|
||
a := encodePrimitive(tc.input)
|
||
hash := h.(*hasher).h()
|
||
hash.Write(a)
|
||
d1 := hash.Sum(nil)
|
||
|
||
// compute hash on the input
|
||
d2, err := h.(*hasher).HashFile(tc.input)
|
||
if err != nil {
|
||
t.Fatalf("Unexpected error %v", err)
|
||
}
|
||
|
||
if !bytes.Equal(d1, d2) {
|
||
t.Fatalf("Digests are not equal. Expected: %x but got: %x", d1, d2)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestHashFileBytes(t *testing.T) {
|
||
|
||
mapInput := map[string]interface{}{
|
||
"key1": []interface{}{
|
||
"element1",
|
||
"element2",
|
||
},
|
||
"key2": map[string]interface{}{
|
||
"a": 0,
|
||
"b": 1,
|
||
"c": json.Number("123.45678911111111111111111111111111111111111111111111111"),
|
||
},
|
||
}
|
||
|
||
arrayInput := []interface{}{
|
||
[]string{"foo", "bar"},
|
||
mapInput,
|
||
`package example`,
|
||
[]string{"$", "α", "©", "™"},
|
||
}
|
||
|
||
arrayBytes, _ := json.Marshal(arrayInput)
|
||
mapBytes, _ := json.Marshal(mapInput)
|
||
|
||
tests := map[string]struct {
|
||
input []byte
|
||
algorithm HashingAlgorithm
|
||
}{
|
||
"map_byte_array": {mapBytes, SHA256},
|
||
"array_byte_array": {arrayBytes, MD5},
|
||
}
|
||
|
||
for name, tc := range tests {
|
||
t.Run(name, func(t *testing.T) {
|
||
|
||
h, _ := NewSignatureHasher(tc.algorithm)
|
||
|
||
// compute hash from the raw bytes
|
||
hash := h.(*hasher).h()
|
||
hash.Write(tc.input)
|
||
d1 := hash.Sum(nil)
|
||
|
||
// compute hash on the input
|
||
d2, err := h.(*hasher).HashFile(tc.input)
|
||
if err != nil {
|
||
t.Fatalf("Unexpected error %v", err)
|
||
}
|
||
|
||
if !bytes.Equal(d1, d2) {
|
||
t.Fatalf("Digests are not equal. Expected: %x but got: %x", d1, d2)
|
||
}
|
||
})
|
||
}
|
||
}
|