1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
// Copyright 2024 OpenPubkey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
package gq
import (
"bytes"
"fmt"
"math/big"
"github.com/lestrrat-go/jwx/v2/jws"
"github.com/openpubkey/openpubkey/util"
)
// Verify verifies a GQ1 signature over a message, using the public identity of the signer.
//
// Comments throughout refer to stages as specified in the ISO/IEC 14888-2 standard.
func (sv *signerVerifier) Verify(proof []byte, identity []byte, message []byte) bool {
n, v, t := modAsInt(sv.n), sv.v, sv.t
nBytes, vBytes := sv.nBytes, sv.vBytes
M := message
// Stage 0 - reject proof if it's the wrong size based on t
R, S, err := sv.decodeProof(proof)
if err != nil {
return false
}
// Stage 1 - create public number G
// currently this hardcoded to use PKCS#1 v1.5 padding as the format mechanism
paddedIdentity := encodePKCS1v15(nBytes, identity)
G := new(big.Int).SetBytes(paddedIdentity)
// Stage 2 - parse signature numbers and recalculate test number W*
// split R into t strings, each consisting of vBytes bytes
Rs := make([]*big.Int, t)
for i := 0; i < t; i++ {
Rs[i] = new(big.Int).SetBytes(R[i*vBytes : (i+1)*vBytes])
}
// split S into t strings, each consisting of nBytes bytes
Ss := make([]*big.Int, t)
for i := 0; i < t; i++ {
s_i := new(big.Int).SetBytes(S[i*nBytes : (i+1)*nBytes])
// reject if S_i = 0 or >= n
if s_i.Cmp(big.NewInt(0)) == 0 || s_i.Cmp(n) != -1 {
return false
}
Ss[i] = s_i
}
// recalculate test number W*
// for i from 1 to t, compute W*_i <- S_i^v * G^{R_i} mod n
// combine to form W*
var Wstar []byte
for i := 0; i < t; i++ {
l := new(big.Int).Exp(Ss[i], v, n)
r := new(big.Int).Exp(G, Rs[i], n)
Wstar_i := new(big.Int).Mul(l, r)
Wstar_i.Mod(Wstar_i, n)
b := make([]byte, nBytes)
Wstar = append(Wstar, Wstar_i.FillBytes(b)...)
}
// Stage 3 - recalculate question number R*
// hash W* and M and take first t*vBytes bytes as R*
Rstar, err := hash(t*vBytes, Wstar, M)
if err != nil {
// TODO: this can only happen if there's some error reading /dev/urandom or something
// so should we return the proper error?
return false
}
// Stage 4 - accept or reject depending on whether R and R* are identical
return bytes.Equal(R, Rstar)
}
func (sv *signerVerifier) VerifyJWT(jwt []byte) bool {
origHeaders, err := OriginalJWTHeaders(jwt)
if err != nil {
return false
}
_, payload, signature, err := jws.SplitCompact(jwt)
if err != nil {
return false
}
signingPayload := util.JoinJWTSegments(origHeaders, payload)
return sv.Verify(signature, signingPayload, signingPayload)
}
func (sv *signerVerifier) decodeProof(s []byte) (R, S []byte, err error) {
bin, err := util.Base64DecodeForJWT(s)
if err != nil {
return nil, nil, err
}
rSize := sv.vBytes * sv.t
sSize := sv.nBytes * sv.t
if len(bin) != rSize+sSize {
return nil, nil, fmt.Errorf("not the correct size")
}
R = bin[:rSize]
S = bin[rSize:]
return R, S, nil
}
|