Files

129 lines
3.2 KiB
Go

package cms
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/tls"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"io"
"math/big"
"net/http"
"github.com/blacktop/ipsw/internal/codesign/cms/oid"
"github.com/blacktop/ipsw/internal/download"
)
type MessageImprint struct {
HashAlgorithm pkix.AlgorithmIdentifier
HashedMessage []byte
}
type Request struct {
Version int
MessageImprint MessageImprint
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
Nonce *big.Int `asn1:"optional"`
CertReq bool `asn1:"optional,default:false"`
Extensions []pkix.Extension `asn1:"tag:1,optional"`
}
type PKIFreeText []asn1.RawValue
type PKIStatusInfo struct {
Status int
StatusString PKIFreeText `asn1:"optional"`
FailInfo asn1.BitString `asn1:"optional"`
}
type Response struct {
Status PKIStatusInfo
TimeStampToken ContentInfo `asn1:"optional"`
}
func GetTimestamp(url, proxy string, insecure bool, si SignerInfo) (Attribute, error) {
hash, err := si.Hash()
if err != nil {
return Attribute{}, fmt.Errorf("failed to get hash: %v", err)
}
mi, err := newMessageImprint(hash, bytes.NewReader(si.Signature))
if err != nil {
return Attribute{}, fmt.Errorf("failed to create message imprint: %v", err)
}
nonce, err := rand.Int(rand.Reader, big.NewInt(1000))
if err != nil {
return Attribute{}, fmt.Errorf("failed to generate nonce: %v", err)
}
reqDER, err := asn1.Marshal(Request{
Version: 1,
CertReq: true,
Nonce: nonce,
MessageImprint: mi,
})
if err != nil {
return Attribute{}, err
}
req, err := http.NewRequest("POST", url, bytes.NewReader(reqDER))
if err != nil {
return Attribute{}, fmt.Errorf("failed to create http POST request: %v", err)
}
req.Header.Add("Content-Type", "application/timestamp-query")
client := &http.Client{
Transport: &http.Transport{
Proxy: download.GetProxy(proxy),
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
},
}
resp, err := client.Do(req)
if err != nil {
return Attribute{}, fmt.Errorf("failed to do http POST request: %v", err)
}
defer resp.Body.Close()
document, err := io.ReadAll(resp.Body)
if err != nil {
return Attribute{}, fmt.Errorf("failed to read http response body: %v", err)
}
var tsresp Response
der, err := ber2Der(document)
if err != nil {
return Attribute{}, err
}
rest, err := asn1.Unmarshal(der, &tsresp)
if err != nil {
return Attribute{}, err
}
if len(rest) > 0 {
return Attribute{}, fmt.Errorf("trailing data after timestamp response")
}
return NewAttribute(oid.AttributeTimeStampToken, tsresp.TimeStampToken)
}
func newMessageImprint(hash crypto.Hash, r io.Reader) (MessageImprint, error) {
digestAlgorithm := oid.CryptoHashToDigestAlgorithm[hash]
if len(digestAlgorithm) == 0 {
return MessageImprint{}, fmt.Errorf("unsupported hash algorithm: %s", hash.String())
}
if !hash.Available() {
return MessageImprint{}, fmt.Errorf("unsupported hash algorithm: %s", hash.String())
}
h := hash.New()
if _, err := io.Copy(h, r); err != nil {
return MessageImprint{}, err
}
return MessageImprint{
HashAlgorithm: pkix.AlgorithmIdentifier{Algorithm: digestAlgorithm},
HashedMessage: h.Sum(nil),
}, nil
}